-- =============================================================
-- ARCHIVO: 27-cfdi-core.sql
-- DESCRIPCION: Modulo CFDI - Facturacion electronica SAT Mexico
--              Certificados CSD, tipos, extension de facturas
-- VERSION: 1.0.0
-- PROYECTO: ERP-Core V2
-- FECHA: 2026-02-03
-- DEPENDE DE: 01-auth-profiles.sql, 24-invoices.sql, 26-fiscal-catalogs.sql
-- =============================================================

-- =====================
-- NOTA: El schema fiscal ya existe en 26-fiscal-catalogs.sql
-- Aqui solo agregamos las tablas especificas de CFDI
-- =====================

-- =====================
-- TIPOS ENUMERADOS CFDI
-- =====================

-- Tipo de comprobante CFDI
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'cfdi_voucher_type') THEN
        CREATE TYPE fiscal.cfdi_voucher_type AS ENUM (
            'I',  -- Ingreso
            'E',  -- Egreso
            'T',  -- Traslado
            'N',  -- Nomina
            'P'   -- Pago
        );
    END IF;
END $$;

-- Estado del certificado CSD
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'csd_certificate_status') THEN
        CREATE TYPE fiscal.csd_certificate_status AS ENUM (
            'active',       -- Activo y vigente
            'expired',      -- Expirado
            'revoked',      -- Revocado por el SAT
            'pending',      -- Pendiente de activacion
            'inactive'      -- Desactivado manualmente
        );
    END IF;
END $$;

-- Estado del CFDI
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'cfdi_status') THEN
        CREATE TYPE fiscal.cfdi_status AS ENUM (
            'draft',        -- Borrador, no timbrado
            'pending',      -- Pendiente de timbrado
            'stamping',     -- En proceso de timbrado
            'stamped',      -- Timbrado exitosamente
            'delivered',    -- Entregado al receptor
            'cancel_requested', -- Solicitud de cancelacion
            'cancelled',    -- Cancelado
            'error'         -- Error en timbrado
        );
    END IF;
END $$;

-- Tipo de relacionCFDI
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'cfdi_relation_type') THEN
        CREATE TYPE fiscal.cfdi_relation_type AS ENUM (
            '01',  -- Nota de credito de documentos relacionados
            '02',  -- Nota de debito de documentos relacionados
            '03',  -- Devolucion de mercancia
            '04',  -- Sustitucion de CFDI previos
            '05',  -- Traslados de mercancias facturadas
            '06',  -- Factura generada por traslados previos
            '07',  -- CFDI por aplicacion de anticipo
            '08',  -- Factura generada por pagos en parcialidades
            '09'   -- Factura generada por pagos diferidos
        );
    END IF;
END $$;

-- =====================
-- TABLA: cfdi_certificates
-- Certificados de Sello Digital (CSD) del SAT
-- =====================
CREATE TABLE IF NOT EXISTS fiscal.cfdi_certificates (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Identificacion del certificado
    certificate_number VARCHAR(20) NOT NULL,  -- Numero de certificado SAT (20 digitos)
    serial_number VARCHAR(50),                -- Numero de serie del certificado

    -- RFC asociado
    rfc VARCHAR(13) NOT NULL,                 -- RFC del contribuyente

    -- Archivos del certificado (almacenados encriptados)
    certificate_pem TEXT NOT NULL,            -- Certificado .cer en formato PEM
    private_key_pem_encrypted TEXT NOT NULL,  -- Llave privada .key encriptada

    -- Metadatos del certificado
    issued_at TIMESTAMPTZ NOT NULL,           -- Fecha de emision
    expires_at TIMESTAMPTZ NOT NULL,          -- Fecha de expiracion

    -- Estado y configuracion
    status fiscal.csd_certificate_status NOT NULL DEFAULT 'pending',
    is_default BOOLEAN DEFAULT FALSE,         -- Certificado por defecto para este tenant

    -- Informacion adicional
    issuer_name VARCHAR(500),                 -- Nombre del emisor (SAT)
    subject_name VARCHAR(500),                -- Nombre del sujeto (contribuyente)

    -- Notas
    description VARCHAR(255),
    notes TEXT,

    -- Auditoria
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,

    -- Solo un certificado activo por defecto por tenant
    UNIQUE(tenant_id, certificate_number),
    CONSTRAINT chk_only_one_default EXCLUDE USING btree (tenant_id WITH =)
        WHERE (is_default = TRUE AND deleted_at IS NULL AND status = 'active')
);

COMMENT ON TABLE fiscal.cfdi_certificates IS 'Certificados de Sello Digital (CSD) para timbrado CFDI';
COMMENT ON COLUMN fiscal.cfdi_certificates.certificate_number IS 'Numero de certificado emitido por el SAT (20 digitos)';
COMMENT ON COLUMN fiscal.cfdi_certificates.certificate_pem IS 'Certificado .cer convertido a formato PEM';
COMMENT ON COLUMN fiscal.cfdi_certificates.private_key_pem_encrypted IS 'Llave privada .key encriptada con AES-256';
COMMENT ON COLUMN fiscal.cfdi_certificates.is_default IS 'Indica si es el certificado predeterminado para facturacion';

-- =====================
-- TABLA: cfdi_pac_configurations
-- Configuracion de Proveedores Autorizados de Certificacion (PAC)
-- =====================
CREATE TABLE IF NOT EXISTS fiscal.cfdi_pac_configurations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Identificacion del PAC
    pac_code VARCHAR(20) NOT NULL,            -- Codigo del PAC (finkok, sat, etc.)
    pac_name VARCHAR(100) NOT NULL,           -- Nombre comercial del PAC

    -- Credenciales (encriptadas)
    username VARCHAR(255),                     -- Usuario del PAC
    password_encrypted TEXT,                   -- Contrasena encriptada
    api_key_encrypted TEXT,                    -- API Key encriptada (si aplica)

    -- Endpoints
    production_url VARCHAR(500),              -- URL produccion
    sandbox_url VARCHAR(500),                 -- URL sandbox/pruebas

    -- Configuracion
    environment VARCHAR(20) DEFAULT 'sandbox', -- sandbox, production
    is_active BOOLEAN DEFAULT TRUE,
    is_default BOOLEAN DEFAULT FALSE,

    -- Contrato
    contract_number VARCHAR(50),
    contract_expires_at DATE,

    -- Limites
    monthly_stamp_limit INTEGER,              -- Limite mensual de timbres
    stamps_used_this_month INTEGER DEFAULT 0,

    -- Auditoria
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,

    UNIQUE(tenant_id, pac_code)
);

COMMENT ON TABLE fiscal.cfdi_pac_configurations IS 'Configuracion de conexion con Proveedores Autorizados de Certificacion (PAC)';
COMMENT ON COLUMN fiscal.cfdi_pac_configurations.pac_code IS 'Codigo identificador del PAC: finkok, sat, facturapi, etc.';
COMMENT ON COLUMN fiscal.cfdi_pac_configurations.environment IS 'Ambiente: sandbox para pruebas, production para produccion';

-- =====================
-- TABLA: cfdi_invoices
-- Extension de facturas con datos CFDI completos
-- =====================
CREATE TABLE IF NOT EXISTS fiscal.cfdi_invoices (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Relacion con factura operacional
    invoice_id UUID NOT NULL REFERENCES billing.invoices(id) ON DELETE CASCADE,

    -- Identificadores CFDI
    uuid VARCHAR(36),                         -- UUID del timbre fiscal (folio fiscal)
    serie VARCHAR(25),                        -- Serie del comprobante
    folio VARCHAR(40),                        -- Folio del comprobante

    -- Tipo de comprobante
    voucher_type fiscal.cfdi_voucher_type NOT NULL DEFAULT 'I',

    -- Certificado usado
    certificate_id UUID REFERENCES fiscal.cfdi_certificates(id),
    certificate_number VARCHAR(20),           -- Numero de certificado usado

    -- Estado del CFDI
    status fiscal.cfdi_status NOT NULL DEFAULT 'draft',

    -- Datos del emisor
    issuer_rfc VARCHAR(13) NOT NULL,
    issuer_name VARCHAR(300) NOT NULL,
    issuer_fiscal_regime VARCHAR(10) NOT NULL,  -- Regimen fiscal del emisor

    -- Datos del receptor
    receiver_rfc VARCHAR(13) NOT NULL,
    receiver_name VARCHAR(300) NOT NULL,
    receiver_fiscal_regime VARCHAR(10),       -- Regimen fiscal del receptor (CFDI 4.0)
    receiver_tax_residence VARCHAR(3),        -- Residencia fiscal extranjero
    receiver_tax_id VARCHAR(40),              -- NumRegIdTrib para extranjeros
    cfdi_use VARCHAR(10) NOT NULL,            -- Uso del CFDI (G01, G02, etc.)
    receiver_zip_code VARCHAR(5),             -- Codigo postal receptor (CFDI 4.0)

    -- Lugar de expedicion
    expedition_place VARCHAR(5) NOT NULL,     -- Codigo postal lugar expedicion

    -- Metodo y forma de pago
    payment_method VARCHAR(10),               -- PUE, PPD
    payment_form VARCHAR(10),                 -- 01, 02, 03, etc.
    payment_conditions VARCHAR(1000),

    -- Moneda y tipo de cambio
    currency VARCHAR(3) NOT NULL DEFAULT 'MXN',
    exchange_rate DECIMAL(10, 6) DEFAULT 1,

    -- Exportacion
    exportation VARCHAR(2) DEFAULT '01',      -- 01=No aplica, 02=Definitiva, etc.

    -- Totales
    subtotal DECIMAL(18, 6) NOT NULL DEFAULT 0,
    discount DECIMAL(18, 6) DEFAULT 0,
    total DECIMAL(18, 6) NOT NULL DEFAULT 0,

    -- Impuestos trasladados (resumen)
    total_transferred_taxes DECIMAL(18, 6) DEFAULT 0,
    -- Impuestos retenidos (resumen)
    total_withheld_taxes DECIMAL(18, 6) DEFAULT 0,

    -- CFDI relacionados
    related_cfdi_type fiscal.cfdi_relation_type,

    -- Confirmacion SAT (para montos grandes)
    confirmation_code VARCHAR(17),

    -- Informacion global (para publico en general)
    global_info_periodicity VARCHAR(10),      -- 01=Diario, 02=Semanal, etc.
    global_info_months VARCHAR(10),           -- 01-12
    global_info_year VARCHAR(4),

    -- XML y timbrado
    xml_original TEXT,                        -- XML antes de timbrar
    xml_stamped TEXT,                         -- XML timbrado
    stamp_date TIMESTAMPTZ,                   -- Fecha de timbrado
    stamp_sat_seal TEXT,                      -- Sello del SAT
    stamp_cfdi_seal TEXT,                     -- Sello del CFDI
    stamp_original_chain TEXT,                -- Cadena original del timbre
    sat_certificate_number VARCHAR(20),       -- Numero certificado SAT

    -- PDF
    pdf_url VARCHAR(500),
    pdf_generated_at TIMESTAMPTZ,

    -- Cancelacion
    cancellation_status VARCHAR(50),
    cancellation_date TIMESTAMPTZ,
    cancellation_ack_xml TEXT,

    -- Validacion SAT
    last_sat_validation TIMESTAMPTZ,
    sat_validation_status VARCHAR(50),
    sat_validation_response JSONB,

    -- Errores
    last_error TEXT,
    error_details JSONB,

    -- Auditoria
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,

    UNIQUE(tenant_id, uuid),
    UNIQUE(invoice_id)
);

COMMENT ON TABLE fiscal.cfdi_invoices IS 'Extension de facturas con datos completos de CFDI 4.0';
COMMENT ON COLUMN fiscal.cfdi_invoices.uuid IS 'UUID del timbre fiscal digital (folio fiscal unico del SAT)';
COMMENT ON COLUMN fiscal.cfdi_invoices.voucher_type IS 'Tipo de comprobante: I=Ingreso, E=Egreso, T=Traslado, N=Nomina, P=Pago';
COMMENT ON COLUMN fiscal.cfdi_invoices.cfdi_use IS 'Uso del CFDI segun catalogo SAT c_UsoCFDI';
COMMENT ON COLUMN fiscal.cfdi_invoices.payment_method IS 'Metodo de pago: PUE=Pago en Una Exhibicion, PPD=Pago en Parcialidades o Diferido';
COMMENT ON COLUMN fiscal.cfdi_invoices.exportation IS 'Tipo de exportacion: 01=No aplica, 02=Definitiva, 03=Temporal, 04=Definitiva con clave';

-- =====================
-- TABLA: cfdi_invoice_items
-- Conceptos/lineas del CFDI con datos fiscales completos
-- =====================
CREATE TABLE IF NOT EXISTS fiscal.cfdi_invoice_items (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    cfdi_invoice_id UUID NOT NULL REFERENCES fiscal.cfdi_invoices(id) ON DELETE CASCADE,

    -- Referencia a linea de factura original
    invoice_item_id UUID,                     -- FK a billing.invoice_items si existe

    -- Datos del concepto
    line_number INTEGER NOT NULL DEFAULT 1,

    -- Claves SAT
    product_service_key VARCHAR(10) NOT NULL, -- Clave producto/servicio SAT
    product_service_name VARCHAR(1000),       -- Descripcion del catalogo SAT
    unit_key VARCHAR(10) NOT NULL,            -- Clave unidad SAT
    unit_name VARCHAR(50),                    -- Nombre unidad

    -- Identificacion del producto
    sku VARCHAR(100),                         -- NoIdentificacion
    description VARCHAR(1000) NOT NULL,       -- Descripcion

    -- Cantidades y montos
    quantity DECIMAL(18, 6) NOT NULL,
    unit_price DECIMAL(18, 6) NOT NULL,
    subtotal DECIMAL(18, 6) NOT NULL,         -- Importe = quantity * unit_price
    discount DECIMAL(18, 6) DEFAULT 0,

    -- Objeto de impuesto
    tax_object VARCHAR(2) NOT NULL DEFAULT '02', -- 01=No objeto, 02=Si objeto, 03=Si objeto no desglosado

    -- Cuenta predial (para inmuebles)
    property_tax_account VARCHAR(150),

    -- Informacion aduanera
    customs_info JSONB,                       -- Array de {NumeroPedimento, FechaPedimento, Aduana}

    -- Parte (para componentes)
    parts JSONB,                              -- Array de partes si aplica

    -- Auditoria
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

COMMENT ON TABLE fiscal.cfdi_invoice_items IS 'Conceptos del CFDI con claves SAT y desglose fiscal';
COMMENT ON COLUMN fiscal.cfdi_invoice_items.product_service_key IS 'Clave de producto/servicio del catalogo SAT c_ClaveProdServ';
COMMENT ON COLUMN fiscal.cfdi_invoice_items.unit_key IS 'Clave de unidad del catalogo SAT c_ClaveUnidad';
COMMENT ON COLUMN fiscal.cfdi_invoice_items.tax_object IS 'ObjetoImp: 01=No objeto de impuesto, 02=Si objeto, 03=Si objeto no desglosado';

-- =====================
-- TABLA: cfdi_invoice_item_taxes
-- Impuestos por concepto del CFDI
-- =====================
CREATE TABLE IF NOT EXISTS fiscal.cfdi_invoice_item_taxes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    cfdi_invoice_item_id UUID NOT NULL REFERENCES fiscal.cfdi_invoice_items(id) ON DELETE CASCADE,

    -- Tipo de impuesto
    tax_type VARCHAR(20) NOT NULL,            -- transferred (traslado) o withheld (retencion)

    -- Impuesto
    tax_code VARCHAR(10) NOT NULL,            -- 001=ISR, 002=IVA, 003=IEPS
    tax_name VARCHAR(50),

    -- Factor
    factor_type VARCHAR(10) NOT NULL,         -- Tasa, Cuota, Exento
    rate DECIMAL(10, 6),                      -- Tasa o cuota

    -- Montos
    base_amount DECIMAL(18, 6) NOT NULL,      -- Base del impuesto
    tax_amount DECIMAL(18, 6) NOT NULL,       -- Importe del impuesto

    -- Auditoria
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

COMMENT ON TABLE fiscal.cfdi_invoice_item_taxes IS 'Impuestos trasladados y retenidos por concepto del CFDI';
COMMENT ON COLUMN fiscal.cfdi_invoice_item_taxes.tax_type IS 'transferred=Impuesto trasladado, withheld=Impuesto retenido';
COMMENT ON COLUMN fiscal.cfdi_invoice_item_taxes.tax_code IS 'Codigo SAT: 001=ISR, 002=IVA, 003=IEPS';
COMMENT ON COLUMN fiscal.cfdi_invoice_item_taxes.factor_type IS 'Tipo de factor: Tasa, Cuota, o Exento';

-- =====================
-- TABLA: cfdi_related_documents
-- CFDI relacionados
-- =====================
CREATE TABLE IF NOT EXISTS fiscal.cfdi_related_documents (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    cfdi_invoice_id UUID NOT NULL REFERENCES fiscal.cfdi_invoices(id) ON DELETE CASCADE,

    -- UUID del CFDI relacionado
    related_uuid VARCHAR(36) NOT NULL,

    -- Tipo de relacion
    relation_type fiscal.cfdi_relation_type NOT NULL,

    -- Referencia interna (si existe en nuestro sistema)
    related_cfdi_invoice_id UUID REFERENCES fiscal.cfdi_invoices(id),

    -- Auditoria
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

COMMENT ON TABLE fiscal.cfdi_related_documents IS 'Relaciones entre CFDIs (notas de credito, sustituciones, etc.)';
COMMENT ON COLUMN fiscal.cfdi_related_documents.relation_type IS 'Tipo de relacion segun catalogo SAT c_TipoRelacion';

-- =====================
-- INDICES CFDI CORE
-- =====================

-- cfdi_certificates
CREATE INDEX IF NOT EXISTS idx_cfdi_certificates_tenant ON fiscal.cfdi_certificates(tenant_id);
CREATE INDEX IF NOT EXISTS idx_cfdi_certificates_rfc ON fiscal.cfdi_certificates(rfc);
CREATE INDEX IF NOT EXISTS idx_cfdi_certificates_status ON fiscal.cfdi_certificates(status);
CREATE INDEX IF NOT EXISTS idx_cfdi_certificates_expires ON fiscal.cfdi_certificates(expires_at);
CREATE INDEX IF NOT EXISTS idx_cfdi_certificates_default ON fiscal.cfdi_certificates(tenant_id, is_default)
    WHERE is_default = TRUE AND deleted_at IS NULL;

-- cfdi_pac_configurations
CREATE INDEX IF NOT EXISTS idx_cfdi_pac_tenant ON fiscal.cfdi_pac_configurations(tenant_id);
CREATE INDEX IF NOT EXISTS idx_cfdi_pac_code ON fiscal.cfdi_pac_configurations(pac_code);
CREATE INDEX IF NOT EXISTS idx_cfdi_pac_active ON fiscal.cfdi_pac_configurations(tenant_id, is_active)
    WHERE is_active = TRUE;

-- cfdi_invoices
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_tenant ON fiscal.cfdi_invoices(tenant_id);
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_invoice ON fiscal.cfdi_invoices(invoice_id);
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_uuid ON fiscal.cfdi_invoices(uuid);
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_serie_folio ON fiscal.cfdi_invoices(serie, folio);
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_status ON fiscal.cfdi_invoices(status);
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_issuer ON fiscal.cfdi_invoices(issuer_rfc);
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_receiver ON fiscal.cfdi_invoices(receiver_rfc);
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_stamp_date ON fiscal.cfdi_invoices(stamp_date);
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_voucher_type ON fiscal.cfdi_invoices(voucher_type);
CREATE INDEX IF NOT EXISTS idx_cfdi_invoices_pending ON fiscal.cfdi_invoices(tenant_id, status)
    WHERE status IN ('draft', 'pending', 'stamping');

-- cfdi_invoice_items
CREATE INDEX IF NOT EXISTS idx_cfdi_items_cfdi ON fiscal.cfdi_invoice_items(cfdi_invoice_id);
CREATE INDEX IF NOT EXISTS idx_cfdi_items_product_key ON fiscal.cfdi_invoice_items(product_service_key);
CREATE INDEX IF NOT EXISTS idx_cfdi_items_line ON fiscal.cfdi_invoice_items(cfdi_invoice_id, line_number);

-- cfdi_invoice_item_taxes
CREATE INDEX IF NOT EXISTS idx_cfdi_item_taxes_item ON fiscal.cfdi_invoice_item_taxes(cfdi_invoice_item_id);
CREATE INDEX IF NOT EXISTS idx_cfdi_item_taxes_code ON fiscal.cfdi_invoice_item_taxes(tax_code);

-- cfdi_related_documents
CREATE INDEX IF NOT EXISTS idx_cfdi_related_cfdi ON fiscal.cfdi_related_documents(cfdi_invoice_id);
CREATE INDEX IF NOT EXISTS idx_cfdi_related_uuid ON fiscal.cfdi_related_documents(related_uuid);

-- =====================
-- FIN DEL ARCHIVO
-- =====================
