-- ===========================================
-- MECANICAS DIESEL - Schema de Compras
-- ===========================================
-- Sistema de ordenes de compra, proveedores y recepciones
-- Gestion completa del ciclo de compras del taller

-- ============================================
-- SCHEMA: purchasing
-- ============================================
CREATE SCHEMA IF NOT EXISTS purchasing;
COMMENT ON SCHEMA purchasing IS 'Gestion de compras: ordenes de compra, recepciones, proveedores';

-- Grants
GRANT USAGE ON SCHEMA purchasing TO mecanicas_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA purchasing TO mecanicas_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA purchasing GRANT ALL ON TABLES TO mecanicas_user;

-- ============================================
-- PROVEEDORES (complementa partners existentes)
-- ============================================

-- Extension de datos de proveedor
CREATE TABLE purchasing.suppliers (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Datos basicos
    code VARCHAR(20) NOT NULL,
    name VARCHAR(256) NOT NULL,
    trade_name VARCHAR(256),                   -- Nombre comercial
    rfc VARCHAR(13),

    -- Contacto
    contact_name VARCHAR(256),
    email VARCHAR(256),
    phone VARCHAR(50),
    mobile VARCHAR(50),

    -- Direccion
    street VARCHAR(256),
    city VARCHAR(100),
    state VARCHAR(100),
    zip_code VARCHAR(10),
    country VARCHAR(100) DEFAULT 'Mexico',

    -- Datos comerciales
    payment_term_days INTEGER DEFAULT 30,
    credit_limit DECIMAL(20,6) DEFAULT 0,
    currency_code VARCHAR(3) DEFAULT 'MXN',

    -- Clasificacion
    category VARCHAR(50),                      -- refacciones, lubricantes, herramientas, etc.
    is_preferred BOOLEAN NOT NULL DEFAULT false,
    rating INTEGER CHECK (rating BETWEEN 1 AND 5),

    -- Notas
    notes TEXT,

    -- Metadatos
    is_active BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ,
    created_by UUID,

    CONSTRAINT uq_supplier_code UNIQUE(tenant_id, code)
);

COMMENT ON TABLE purchasing.suppliers IS 'Proveedores del taller';

-- Indices para suppliers
CREATE INDEX idx_suppliers_tenant ON purchasing.suppliers(tenant_id);
CREATE INDEX idx_suppliers_name ON purchasing.suppliers(name);
CREATE INDEX idx_suppliers_category ON purchasing.suppliers(category);
CREATE INDEX idx_suppliers_preferred ON purchasing.suppliers(is_preferred) WHERE is_preferred = true;

-- RLS para suppliers
SELECT create_tenant_rls_policies('purchasing', 'suppliers');

-- Trigger para updated_at
CREATE TRIGGER set_updated_at_suppliers
    BEFORE UPDATE ON purchasing.suppliers
    FOR EACH ROW
    EXECUTE FUNCTION trigger_set_updated_at();

-- ============================================
-- ORDENES DE COMPRA
-- ============================================

-- Estados de orden de compra
CREATE TYPE purchasing.po_status AS ENUM (
    'draft',        -- Borrador
    'sent',         -- Enviada a proveedor
    'confirmed',    -- Confirmada por proveedor
    'partial',      -- Parcialmente recibida
    'received',     -- Completamente recibida
    'cancelled'     -- Cancelada
);

-- Ordenes de compra
CREATE TABLE purchasing.purchase_orders (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Identificacion
    order_number VARCHAR(50) NOT NULL,
    reference VARCHAR(100),                    -- Referencia del proveedor

    -- Proveedor
    supplier_id UUID NOT NULL REFERENCES purchasing.suppliers(id),

    -- Estado
    status purchasing.po_status NOT NULL DEFAULT 'draft',

    -- Fechas
    order_date DATE NOT NULL DEFAULT CURRENT_DATE,
    expected_date DATE,                        -- Fecha esperada de entrega
    received_date DATE,                        -- Fecha de recepcion completa

    -- Importes
    subtotal DECIMAL(20,6) NOT NULL DEFAULT 0,
    discount_amount DECIMAL(20,6) NOT NULL DEFAULT 0,
    tax_amount DECIMAL(20,6) NOT NULL DEFAULT 0,
    total DECIMAL(20,6) NOT NULL DEFAULT 0,
    currency_code VARCHAR(3) DEFAULT 'MXN',

    -- Urgencia (para taller)
    priority VARCHAR(20) DEFAULT 'normal',
    service_order_id UUID,                     -- Orden de servicio relacionada (si aplica)

    -- Notas
    notes TEXT,
    internal_notes TEXT,

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ,
    created_by UUID NOT NULL,
    confirmed_by UUID,
    confirmed_at TIMESTAMPTZ,

    CONSTRAINT uq_po_number UNIQUE(tenant_id, order_number),
    CONSTRAINT chk_po_priority CHECK (priority IN ('low', 'normal', 'high', 'urgent'))
);

COMMENT ON TABLE purchasing.purchase_orders IS 'Ordenes de compra a proveedores';
COMMENT ON COLUMN purchasing.purchase_orders.service_order_id IS 'Orden de servicio que origino la compra (para urgencias)';

-- Indices para purchase_orders
CREATE INDEX idx_po_tenant ON purchasing.purchase_orders(tenant_id);
CREATE INDEX idx_po_supplier ON purchasing.purchase_orders(supplier_id);
CREATE INDEX idx_po_status ON purchasing.purchase_orders(status);
CREATE INDEX idx_po_date ON purchasing.purchase_orders(order_date DESC);
CREATE INDEX idx_po_expected ON purchasing.purchase_orders(expected_date) WHERE status NOT IN ('received', 'cancelled');
CREATE INDEX idx_po_service_order ON purchasing.purchase_orders(service_order_id) WHERE service_order_id IS NOT NULL;

-- RLS para purchase_orders
SELECT create_tenant_rls_policies('purchasing', 'purchase_orders');

-- Triggers
CREATE TRIGGER set_updated_at_purchase_orders
    BEFORE UPDATE ON purchasing.purchase_orders
    FOR EACH ROW
    EXECUTE FUNCTION trigger_set_updated_at();

-- ============================================
-- LINEAS DE ORDEN DE COMPRA
-- ============================================

CREATE TABLE purchasing.purchase_order_lines (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    purchase_order_id UUID NOT NULL REFERENCES purchasing.purchase_orders(id) ON DELETE CASCADE,

    -- Linea
    line_number INTEGER NOT NULL DEFAULT 1,

    -- Producto
    part_id UUID,                              -- Referencia a parts_management.parts
    product_code VARCHAR(50),                  -- Codigo del producto (desnormalizado)
    description VARCHAR(500) NOT NULL,

    -- Cantidades
    quantity DECIMAL(20,6) NOT NULL,
    unit_of_measure VARCHAR(20) DEFAULT 'PZA',
    received_quantity DECIMAL(20,6) NOT NULL DEFAULT 0,

    -- Precios
    unit_price DECIMAL(20,6) NOT NULL,
    discount_percent DECIMAL(5,2) NOT NULL DEFAULT 0,
    subtotal DECIMAL(20,6) NOT NULL,
    tax_percent DECIMAL(5,2) NOT NULL DEFAULT 16.00,  -- IVA Mexico
    tax_amount DECIMAL(20,6) NOT NULL DEFAULT 0,
    total DECIMAL(20,6) NOT NULL,

    -- Fechas
    expected_date DATE,

    -- Estado de linea
    is_closed BOOLEAN NOT NULL DEFAULT false,

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE purchasing.purchase_order_lines IS 'Lineas de detalle de ordenes de compra';

-- Indices para lines
CREATE INDEX idx_pol_order ON purchasing.purchase_order_lines(purchase_order_id);
CREATE INDEX idx_pol_part ON purchasing.purchase_order_lines(part_id) WHERE part_id IS NOT NULL;
CREATE INDEX idx_pol_pending ON purchasing.purchase_order_lines(purchase_order_id)
    WHERE received_quantity < quantity AND is_closed = false;

-- ============================================
-- RECEPCIONES DE COMPRA
-- ============================================

CREATE TABLE purchasing.purchase_receipts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Identificacion
    receipt_number VARCHAR(50) NOT NULL,
    purchase_order_id UUID NOT NULL REFERENCES purchasing.purchase_orders(id),

    -- Fecha y proveedor
    receipt_date DATE NOT NULL DEFAULT CURRENT_DATE,
    supplier_id UUID NOT NULL REFERENCES purchasing.suppliers(id),

    -- Documentos del proveedor
    supplier_invoice VARCHAR(50),              -- Numero de factura proveedor
    supplier_delivery_note VARCHAR(50),        -- Remision del proveedor

    -- Notas
    notes TEXT,

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,

    CONSTRAINT uq_receipt_number UNIQUE(tenant_id, receipt_number)
);

COMMENT ON TABLE purchasing.purchase_receipts IS 'Recepciones de mercancia de ordenes de compra';

-- Indices para receipts
CREATE INDEX idx_pr_tenant ON purchasing.purchase_receipts(tenant_id);
CREATE INDEX idx_pr_order ON purchasing.purchase_receipts(purchase_order_id);
CREATE INDEX idx_pr_date ON purchasing.purchase_receipts(receipt_date DESC);

-- RLS para receipts
SELECT create_tenant_rls_policies('purchasing', 'purchase_receipts');

-- Lineas de recepcion
CREATE TABLE purchasing.purchase_receipt_lines (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    receipt_id UUID NOT NULL REFERENCES purchasing.purchase_receipts(id) ON DELETE CASCADE,
    order_line_id UUID NOT NULL REFERENCES purchasing.purchase_order_lines(id),

    -- Cantidades
    quantity_received DECIMAL(20,6) NOT NULL,
    quantity_rejected DECIMAL(20,6) NOT NULL DEFAULT 0,
    rejection_reason VARCHAR(256),

    -- Lote/Serie (si aplica)
    lot_number VARCHAR(100),
    serial_numbers TEXT[],

    -- Ubicacion destino
    location_id UUID,                          -- Referencia a inventory location

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE purchasing.purchase_receipt_lines IS 'Detalle de productos recibidos';

CREATE INDEX idx_prl_receipt ON purchasing.purchase_receipt_lines(receipt_id);
CREATE INDEX idx_prl_order_line ON purchasing.purchase_receipt_lines(order_line_id);

-- ============================================
-- FUNCIONES AUXILIARES
-- ============================================

-- Funcion para generar numero de orden de compra
CREATE OR REPLACE FUNCTION purchasing.generate_po_number(p_tenant_id UUID)
RETURNS VARCHAR(50) AS $$
DECLARE
    v_year TEXT;
    v_sequence INTEGER;
    v_number VARCHAR(50);
BEGIN
    v_year := TO_CHAR(CURRENT_DATE, 'YYYY');

    -- Obtener siguiente secuencia del año
    SELECT COALESCE(MAX(
        CAST(SUBSTRING(order_number FROM 'OC-' || v_year || '-(\d+)') AS INTEGER)
    ), 0) + 1
    INTO v_sequence
    FROM purchasing.purchase_orders
    WHERE tenant_id = p_tenant_id
      AND order_number LIKE 'OC-' || v_year || '-%';

    v_number := 'OC-' || v_year || '-' || LPAD(v_sequence::TEXT, 5, '0');

    RETURN v_number;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION purchasing.generate_po_number IS 'Genera numero secuencial de orden de compra (OC-YYYY-NNNNN)';

-- Funcion para generar numero de recepcion
CREATE OR REPLACE FUNCTION purchasing.generate_receipt_number(p_tenant_id UUID)
RETURNS VARCHAR(50) AS $$
DECLARE
    v_year TEXT;
    v_sequence INTEGER;
    v_number VARCHAR(50);
BEGIN
    v_year := TO_CHAR(CURRENT_DATE, 'YYYY');

    SELECT COALESCE(MAX(
        CAST(SUBSTRING(receipt_number FROM 'REC-' || v_year || '-(\d+)') AS INTEGER)
    ), 0) + 1
    INTO v_sequence
    FROM purchasing.purchase_receipts
    WHERE tenant_id = p_tenant_id
      AND receipt_number LIKE 'REC-' || v_year || '-%';

    v_number := 'REC-' || v_year || '-' || LPAD(v_sequence::TEXT, 5, '0');

    RETURN v_number;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION purchasing.generate_receipt_number IS 'Genera numero secuencial de recepcion (REC-YYYY-NNNNN)';

-- Funcion para calcular totales de linea
CREATE OR REPLACE FUNCTION purchasing.calculate_line_totals()
RETURNS TRIGGER AS $$
BEGIN
    -- Calcular subtotal (con descuento)
    NEW.subtotal := NEW.quantity * NEW.unit_price * (1 - NEW.discount_percent / 100);

    -- Calcular impuesto
    NEW.tax_amount := NEW.subtotal * NEW.tax_percent / 100;

    -- Calcular total
    NEW.total := NEW.subtotal + NEW.tax_amount;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER calculate_pol_totals
    BEFORE INSERT OR UPDATE OF quantity, unit_price, discount_percent, tax_percent
    ON purchasing.purchase_order_lines
    FOR EACH ROW
    EXECUTE FUNCTION purchasing.calculate_line_totals();

-- Funcion para actualizar totales de orden
CREATE OR REPLACE FUNCTION purchasing.update_order_totals()
RETURNS TRIGGER AS $$
BEGIN
    UPDATE purchasing.purchase_orders po
    SET
        subtotal = COALESCE((
            SELECT SUM(subtotal) FROM purchasing.purchase_order_lines WHERE purchase_order_id = po.id
        ), 0),
        tax_amount = COALESCE((
            SELECT SUM(tax_amount) FROM purchasing.purchase_order_lines WHERE purchase_order_id = po.id
        ), 0),
        total = COALESCE((
            SELECT SUM(total) FROM purchasing.purchase_order_lines WHERE purchase_order_id = po.id
        ), 0),
        updated_at = NOW()
    WHERE id = COALESCE(NEW.purchase_order_id, OLD.purchase_order_id);

    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_po_totals
    AFTER INSERT OR UPDATE OR DELETE
    ON purchasing.purchase_order_lines
    FOR EACH ROW
    EXECUTE FUNCTION purchasing.update_order_totals();

-- Funcion para actualizar cantidades recibidas
CREATE OR REPLACE FUNCTION purchasing.update_received_quantities()
RETURNS TRIGGER AS $$
DECLARE
    v_order_id UUID;
    v_total_lines INTEGER;
    v_received_lines INTEGER;
BEGIN
    -- Actualizar cantidad recibida en linea de orden
    UPDATE purchasing.purchase_order_lines pol
    SET received_quantity = COALESCE((
        SELECT SUM(prl.quantity_received)
        FROM purchasing.purchase_receipt_lines prl
        WHERE prl.order_line_id = pol.id
    ), 0)
    WHERE id = NEW.order_line_id;

    -- Obtener orden de compra
    SELECT purchase_order_id INTO v_order_id
    FROM purchasing.purchase_order_lines
    WHERE id = NEW.order_line_id;

    -- Verificar si toda la orden fue recibida
    SELECT
        COUNT(*),
        COUNT(*) FILTER (WHERE received_quantity >= quantity)
    INTO v_total_lines, v_received_lines
    FROM purchasing.purchase_order_lines
    WHERE purchase_order_id = v_order_id;

    -- Actualizar estado de la orden
    UPDATE purchasing.purchase_orders
    SET status = CASE
        WHEN v_received_lines = v_total_lines THEN 'received'::purchasing.po_status
        WHEN v_received_lines > 0 THEN 'partial'::purchasing.po_status
        ELSE status
    END,
    received_date = CASE
        WHEN v_received_lines = v_total_lines THEN CURRENT_DATE
        ELSE received_date
    END,
    updated_at = NOW()
    WHERE id = v_order_id;

    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_po_received
    AFTER INSERT
    ON purchasing.purchase_receipt_lines
    FOR EACH ROW
    EXECUTE FUNCTION purchasing.update_received_quantities();

-- ============================================
-- VISTAS UTILES
-- ============================================

-- Vista de ordenes de compra pendientes
CREATE VIEW purchasing.v_pending_orders AS
SELECT
    po.id,
    po.tenant_id,
    po.order_number,
    po.status,
    po.order_date,
    po.expected_date,
    s.name as supplier_name,
    s.contact_name,
    s.phone as supplier_phone,
    po.total,
    po.priority,
    po.service_order_id,
    CASE
        WHEN po.expected_date < CURRENT_DATE THEN 'overdue'
        WHEN po.expected_date = CURRENT_DATE THEN 'today'
        WHEN po.expected_date <= CURRENT_DATE + 3 THEN 'soon'
        ELSE 'normal'
    END as urgency,
    COUNT(pol.id) as line_count,
    SUM(CASE WHEN pol.received_quantity < pol.quantity THEN 1 ELSE 0 END) as pending_lines
FROM purchasing.purchase_orders po
JOIN purchasing.suppliers s ON s.id = po.supplier_id
LEFT JOIN purchasing.purchase_order_lines pol ON pol.purchase_order_id = po.id
WHERE po.status NOT IN ('received', 'cancelled')
GROUP BY po.id, po.tenant_id, po.order_number, po.status, po.order_date,
         po.expected_date, s.name, s.contact_name, s.phone, po.total,
         po.priority, po.service_order_id
ORDER BY po.expected_date ASC NULLS LAST, po.priority DESC;

COMMENT ON VIEW purchasing.v_pending_orders IS 'Ordenes de compra pendientes de recibir';

-- Vista de productos pendientes de recibir
CREATE VIEW purchasing.v_pending_products AS
SELECT
    po.tenant_id,
    pol.part_id,
    pol.product_code,
    pol.description,
    po.order_number,
    po.supplier_id,
    s.name as supplier_name,
    pol.quantity,
    pol.received_quantity,
    pol.quantity - pol.received_quantity as pending_quantity,
    pol.unit_price,
    po.expected_date,
    po.service_order_id
FROM purchasing.purchase_order_lines pol
JOIN purchasing.purchase_orders po ON po.id = pol.purchase_order_id
JOIN purchasing.suppliers s ON s.id = po.supplier_id
WHERE pol.received_quantity < pol.quantity
  AND pol.is_closed = false
  AND po.status NOT IN ('cancelled')
ORDER BY po.expected_date ASC NULLS LAST;

COMMENT ON VIEW purchasing.v_pending_products IS 'Productos pendientes de recibir por orden';

-- Vista de historial de compras por proveedor
CREATE VIEW purchasing.v_supplier_history AS
SELECT
    s.id as supplier_id,
    s.tenant_id,
    s.code as supplier_code,
    s.name as supplier_name,
    s.category,
    s.rating,
    COUNT(DISTINCT po.id) as total_orders,
    COUNT(DISTINCT po.id) FILTER (WHERE po.status = 'received') as completed_orders,
    SUM(po.total) FILTER (WHERE po.status = 'received') as total_purchased,
    AVG(po.received_date - po.expected_date) FILTER (WHERE po.status = 'received' AND po.expected_date IS NOT NULL) as avg_delivery_days,
    MAX(po.order_date) as last_order_date
FROM purchasing.suppliers s
LEFT JOIN purchasing.purchase_orders po ON po.supplier_id = s.id
WHERE s.is_active = true
GROUP BY s.id, s.tenant_id, s.code, s.name, s.category, s.rating
ORDER BY total_purchased DESC NULLS LAST;

COMMENT ON VIEW purchasing.v_supplier_history IS 'Historial y estadisticas de compras por proveedor';

-- ============================================
-- GRANTS ADICIONALES
-- ============================================
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA purchasing TO mecanicas_user;
GRANT SELECT ON ALL TABLES IN SCHEMA purchasing TO mecanicas_user;
