-- ============================================================
-- БАЗА ДАННЫХ: Продажа компьютерной техники
-- СУБД: PostgreSQL
-- ============================================================

DROP TABLE IF EXISTS order_items CASCADE;
DROP TABLE IF EXISTS orders CASCADE;
DROP TABLE IF EXISTS app_users CASCADE;
DROP TABLE IF EXISTS products CASCADE;
DROP TABLE IF EXISTS suppliers CASCADE;
DROP TABLE IF EXISTS clients CASCADE;
DROP TABLE IF EXISTS employees CASCADE;

DROP VIEW IF EXISTS v_orders_full_info CASCADE;
DROP VIEW IF EXISTS v_late_orders CASCADE;
DROP VIEW IF EXISTS v_order_items_full CASCADE;
DROP VIEW IF EXISTS v_products_dictionary CASCADE;

DROP FUNCTION IF EXISTS fn_get_late_orders_by_client(INTEGER);
DROP FUNCTION IF EXISTS fn_calculate_order_total(INTEGER);
DROP FUNCTION IF EXISTS fn_update_order_total();
DROP FUNCTION IF EXISTS fn_reduce_product_stock();

CREATE TABLE suppliers (
   supplier_id    SERIAL PRIMARY KEY,
   company_name   VARCHAR(150) NOT NULL,
   contact_person VARCHAR(100) NOT NULL,
   phone          VARCHAR(20) NOT NULL,
   address        VARCHAR(250) NOT NULL
);

CREATE TABLE products (
   product_id     SERIAL PRIMARY KEY,
   product_name   VARCHAR(200) NOT NULL,
   price          NUMERIC(12,2) NOT NULL CHECK (price >= 0),
   stock_quantity INTEGER NOT NULL DEFAULT 0 CHECK (stock_quantity >= 0),
   category       VARCHAR(100) NOT NULL,
   supplier_id    INTEGER NOT NULL REFERENCES suppliers(supplier_id) ON UPDATE CASCADE
);

CREATE TABLE clients (
   client_id   SERIAL PRIMARY KEY,
   first_name  VARCHAR(80) NOT NULL,
   last_name   VARCHAR(80) NOT NULL,
   address     VARCHAR(250) NOT NULL,
   phone       VARCHAR(20),
   email       VARCHAR(150) UNIQUE
);

CREATE TABLE employees (
   employee_id SERIAL PRIMARY KEY,
   last_name   VARCHAR(80) NOT NULL,
   first_name  VARCHAR(80) NOT NULL,
   phone       VARCHAR(20),
   email       VARCHAR(150) UNIQUE,
   position    VARCHAR(100) NOT NULL
);

CREATE TABLE orders (
   order_id    SERIAL PRIMARY KEY,
   order_date  TIMESTAMP NOT NULL DEFAULT NOW(),
   status      VARCHAR(50) NOT NULL DEFAULT 'новый'
       CHECK (status IN ('новый','в обработке','отправлен','выполнен','отменён')),
   total_cost  NUMERIC(14,2) NOT NULL DEFAULT 0 CHECK (total_cost >= 0),
   client_id   INTEGER NOT NULL REFERENCES clients(client_id),
   employee_id INTEGER NOT NULL REFERENCES employees(employee_id)
);

CREATE TABLE order_items (
   order_item_id SERIAL PRIMARY KEY,
   order_id      INTEGER NOT NULL REFERENCES orders(order_id) ON DELETE CASCADE,
   product_id    INTEGER NOT NULL REFERENCES products(product_id),
   unit_price    NUMERIC(12,2) NOT NULL CHECK (unit_price >= 0),
   quantity      INTEGER NOT NULL CHECK (quantity > 0)
);

CREATE TABLE app_users (
   user_id       SERIAL PRIMARY KEY,
   login         VARCHAR(50) NOT NULL UNIQUE,
   password_hash TEXT NOT NULL,
   role_name     VARCHAR(30) NOT NULL CHECK (role_name IN ('admin','manager','viewer'))
);

CREATE OR REPLACE VIEW v_orders_full_info AS
SELECT
    o.order_id,
    o.order_date,
    o.status,
    o.total_cost,
    c.client_id,
    c.last_name || ' ' || c.first_name AS client_name,
    c.phone AS client_phone,
    c.email AS client_email,
    e.employee_id,
    e.last_name || ' ' || e.first_name AS employee_name,
    e.position AS employee_position
FROM orders o
JOIN clients c ON o.client_id = c.client_id
JOIN employees e ON o.employee_id = e.employee_id;

CREATE OR REPLACE VIEW v_late_orders AS
SELECT *
FROM v_orders_full_info
WHERE status NOT IN ('выполнен', 'отменён')
  AND order_date < NOW() - INTERVAL '7 days';

CREATE OR REPLACE VIEW v_order_items_full AS
SELECT
    oi.order_item_id,
    oi.order_id,
    p.product_id,
    p.product_name,
    p.category,
    oi.unit_price,
    oi.quantity,
    oi.unit_price * oi.quantity AS item_sum
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id;

CREATE OR REPLACE VIEW v_products_dictionary AS
SELECT
    p.product_id,
    p.product_name,
    p.category,
    p.price,
    p.stock_quantity,
    s.supplier_id,
    s.company_name AS supplier_name,
    s.contact_person,
    s.phone AS supplier_phone,
    s.address AS supplier_address
FROM products p
JOIN suppliers s ON p.supplier_id = s.supplier_id;

CREATE OR REPLACE FUNCTION fn_get_late_orders_by_client(p_client_id INTEGER)
RETURNS TABLE (
    order_id INTEGER,
    order_date TIMESTAMP,
    status VARCHAR,
    total_cost NUMERIC,
    client_id INTEGER,
    client_name TEXT,
    client_phone VARCHAR,
    client_email VARCHAR,
    employee_id INTEGER,
    employee_name TEXT
)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
    SELECT
        v.order_id,
        v.order_date,
        v.status,
        v.total_cost,
        v.client_id,
        v.client_name,
        v.client_phone,
        v.client_email,
        v.employee_id,
        v.employee_name
    FROM v_late_orders v
    WHERE v.client_id = p_client_id
    ORDER BY v.order_date DESC;
END;
$$;

CREATE OR REPLACE FUNCTION fn_calculate_order_total(p_order_id INTEGER)
RETURNS NUMERIC
LANGUAGE plpgsql
AS $$
DECLARE
    v_total NUMERIC(14,2);
BEGIN
    SELECT COALESCE(SUM(unit_price * quantity), 0)
    INTO v_total
    FROM order_items
    WHERE order_id = p_order_id;

    RETURN v_total;
END;
$$;

CREATE OR REPLACE FUNCTION fn_update_order_total()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
    v_order_id INTEGER;
BEGIN
    v_order_id := COALESCE(NEW.order_id, OLD.order_id);

    UPDATE orders
    SET total_cost = fn_calculate_order_total(v_order_id)
    WHERE order_id = v_order_id;

    RETURN COALESCE(NEW, OLD);
END;
$$;

CREATE OR REPLACE FUNCTION fn_reduce_product_stock()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
    v_stock INTEGER;
BEGIN
    SELECT stock_quantity INTO v_stock
    FROM products
    WHERE product_id = NEW.product_id;

    IF v_stock IS NULL THEN
        RAISE EXCEPTION 'Товар не найден';
    END IF;

    IF v_stock < NEW.quantity THEN
        RAISE EXCEPTION 'Недостаточно товара на складе. Доступно: %, требуется: %', v_stock, NEW.quantity;
    END IF;

    UPDATE products
    SET stock_quantity = stock_quantity - NEW.quantity
    WHERE product_id = NEW.product_id;

    RETURN NEW;
END;
$$;

CREATE TRIGGER trg_update_order_total_insert
AFTER INSERT ON order_items
FOR EACH ROW
EXECUTE FUNCTION fn_update_order_total();

CREATE TRIGGER trg_update_order_total_update
AFTER UPDATE ON order_items
FOR EACH ROW
EXECUTE FUNCTION fn_update_order_total();

CREATE TRIGGER trg_update_order_total_delete
AFTER DELETE ON order_items
FOR EACH ROW
EXECUTE FUNCTION fn_update_order_total();

CREATE TRIGGER trg_reduce_product_stock
BEFORE INSERT ON order_items
FOR EACH ROW
EXECUTE FUNCTION fn_reduce_product_stock();

CREATE INDEX idx_orders_client_id ON orders(client_id);
CREATE INDEX idx_orders_employee_id ON orders(employee_id);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_order_date ON orders(order_date);
CREATE INDEX idx_products_supplier_id ON products(supplier_id);
CREATE INDEX idx_products_category ON products(category);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_order_items_product_id ON order_items(product_id);

INSERT INTO suppliers(company_name, contact_person, phone, address) VALUES
('TechImport', 'Иванов Сергей', '+79001112233', 'Москва, ул. Ленина, д. 10'),
('CompMarket', 'Петров Алексей', '+79004445566', 'Санкт-Петербург, Невский пр., д. 20'),
('DigitalTrade', 'Соколова Анна', '+79007778899', 'Великий Новгород, ул. Большая Санкт-Петербургская, д. 15');

INSERT INTO products(product_name, price, stock_quantity, category, supplier_id) VALUES
('Ноутбук Lenovo IdeaPad 3', 65000.00, 15, 'Ноутбуки', 1),
('Ноутбук ASUS VivoBook', 72000.00, 10, 'Ноутбуки', 1),
('Монитор Samsung 24', 18000.00, 30, 'Мониторы', 2),
('Монитор LG UltraGear', 26000.00, 12, 'Мониторы', 2),
('Клавиатура Logitech K120', 1500.00, 50, 'Периферия', 3),
('Мышь Logitech M185', 1200.00, 60, 'Периферия', 3),
('SSD Kingston 1TB', 7200.00, 40, 'Комплектующие', 2),
('Видеокарта MSI RTX 4060', 42000.00, 8, 'Комплектующие', 1);

INSERT INTO clients(first_name, last_name, address, phone, email) VALUES
('Андрей', 'Смирнов', 'Великий Новгород, ул. Мира, д. 5', '+79110000001', 'smirnov@mail.ru'),
('Ольга', 'Кузнецова', 'Великий Новгород, ул. Парковая, д. 8', '+79110000002', 'kuznetsova@mail.ru'),
('Иван', 'Павлов', 'Великий Новгород, ул. Ломоносова, д. 3', '+79110000003', 'pavlov@mail.ru'),
('Мария', 'Васильева', 'Великий Новгород, ул. Попова, д. 12', '+79110000004', 'vasilieva@mail.ru');

INSERT INTO employees(last_name, first_name, phone, email, position) VALUES
('Иванова', 'Мария', '+79220000001', 'ivanova@shop.ru', 'Менеджер'),
('Соколов', 'Дмитрий', '+79220000002', 'sokolov@shop.ru', 'Администратор'),
('Фёдорова', 'Елена', '+79220000003', 'fedorova@shop.ru', 'Менеджер');

INSERT INTO orders(order_date, status, client_id, employee_id) VALUES
(NOW() - INTERVAL '12 days', 'в обработке', 1, 1),
(NOW() - INTERVAL '10 days', 'отправлен', 1, 1),
(NOW() - INTERVAL '3 days', 'новый', 2, 3),
(NOW() - INTERVAL '15 days', 'выполнен', 2, 1),
(NOW() - INTERVAL '20 days', 'в обработке', 3, 3),
(NOW() - INTERVAL '4 days', 'отменён', 4, 1);

INSERT INTO order_items(order_id, product_id, unit_price, quantity) VALUES
(1, 1, 65000.00, 1),
(1, 5, 1500.00, 2),
(2, 3, 18000.00, 2),
(3, 6, 1200.00, 1),
(3, 7, 7200.00, 1),
(4, 2, 72000.00, 1),
(5, 8, 42000.00, 1),
(5, 4, 26000.00, 1),
(6, 5, 1500.00, 1);

-- ============================================================
-- ПОЛЬЗОВАТЕЛИ ПРИЛОЖЕНИЯ
-- Роли PostgreSQL не создаются, так как в учебной БД это запрещено.
-- Разграничение прав выполняется на уровне приложения через app_users.role_name.
-- Пароли для проверки: admin123, manager123, viewer123.
-- ============================================================

DELETE FROM app_users;

INSERT INTO app_users(login, password_hash, role_name) VALUES
('admin',   '240be518fabd2724ddb6f04eeb1da5967448d7e831c08c8fa822809f74c720a9', 'admin'),
('manager', '866485796cfa8d7c0cf7111640205b83076433547577511d81f8030ae99ecea5', 'manager'),
('viewer',  '65375049b9e4d7cad6c9ba286fdeb9394b28135a3e84136404cfccfdcc438894', 'viewer');

-- Проверочные запросы
SELECT * FROM app_users;
SELECT * FROM v_orders_full_info;
SELECT * FROM v_late_orders;
SELECT * FROM v_products_dictionary;
SELECT * FROM fn_get_late_orders_by_client(1);
