-- ============================================================================
-- ESTIMATES Schema DDL - Estimaciones, Anticipos y Retenciones
-- Modulos: MAI-008 (Estimaciones y Facturación)
-- Version: 1.0.0
-- Fecha: 2025-12-08
-- ============================================================================
-- PREREQUISITOS:
--   1. ERP-Core instalado (auth.tenants, auth.users)
--   2. Schema construction instalado (fraccionamientos, contratos, conceptos, lotes, departamentos)
-- ============================================================================

-- Verificar prerequisitos
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
        RAISE EXCEPTION 'Schema auth no existe. Ejecutar primero ERP-Core DDL';
    END IF;
    IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'construction') THEN
        RAISE EXCEPTION 'Schema construction no existe. Ejecutar primero construction DDL';
    END IF;
END $$;

-- Crear schema
CREATE SCHEMA IF NOT EXISTS estimates;

-- ============================================================================
-- TYPES (ENUMs)
-- ============================================================================

DO $$ BEGIN
    CREATE TYPE estimates.estimate_status AS ENUM (
        'draft', 'submitted', 'reviewed', 'approved', 'invoiced', 'paid', 'rejected', 'cancelled'
    );
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

DO $$ BEGIN
    CREATE TYPE estimates.advance_type AS ENUM (
        'initial', 'progress', 'materials'
    );
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

DO $$ BEGIN
    CREATE TYPE estimates.retention_type AS ENUM (
        'guarantee', 'tax', 'penalty', 'other'
    );
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

DO $$ BEGIN
    CREATE TYPE estimates.generator_status AS ENUM (
        'draft', 'in_progress', 'completed', 'approved'
    );
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

-- ============================================================================
-- TABLES - ESTIMACIONES
-- ============================================================================

-- Tabla: estimaciones (estimaciones de obra)
CREATE TABLE IF NOT EXISTS estimates.estimaciones (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    contrato_id UUID NOT NULL REFERENCES construction.contratos(id),
    fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
    estimate_number VARCHAR(30) NOT NULL,
    period_start DATE NOT NULL,
    period_end DATE NOT NULL,
    sequence_number INTEGER NOT NULL,
    status estimates.estimate_status NOT NULL DEFAULT 'draft',
    subtotal DECIMAL(16,2) DEFAULT 0,
    advance_amount DECIMAL(16,2) DEFAULT 0,
    retention_amount DECIMAL(16,2) DEFAULT 0,
    tax_amount DECIMAL(16,2) DEFAULT 0,
    total_amount DECIMAL(16,2) DEFAULT 0,
    submitted_at TIMESTAMPTZ,
    submitted_by UUID REFERENCES auth.users(id),
    reviewed_at TIMESTAMPTZ,
    reviewed_by UUID REFERENCES auth.users(id),
    approved_at TIMESTAMPTZ,
    approved_by UUID REFERENCES auth.users(id),
    invoice_id UUID,
    invoiced_at TIMESTAMPTZ,
    paid_at TIMESTAMPTZ,
    notes TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,
    deleted_by UUID REFERENCES auth.users(id),
    CONSTRAINT uq_estimaciones_number_tenant UNIQUE (tenant_id, estimate_number),
    CONSTRAINT uq_estimaciones_sequence_contrato UNIQUE (contrato_id, sequence_number),
    CONSTRAINT chk_estimaciones_period CHECK (period_end >= period_start)
);

-- Tabla: estimacion_conceptos (líneas de estimación)
CREATE TABLE IF NOT EXISTS estimates.estimacion_conceptos (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    estimacion_id UUID NOT NULL REFERENCES estimates.estimaciones(id) ON DELETE CASCADE,
    concepto_id UUID NOT NULL REFERENCES construction.conceptos(id),
    contrato_partida_id UUID REFERENCES construction.contrato_partidas(id),
    quantity_contract DECIMAL(12,4) DEFAULT 0,
    quantity_previous DECIMAL(12,4) DEFAULT 0,
    quantity_current DECIMAL(12,4) DEFAULT 0,
    quantity_accumulated DECIMAL(12,4) GENERATED ALWAYS AS (quantity_previous + quantity_current) STORED,
    unit_price DECIMAL(12,4) NOT NULL DEFAULT 0,
    amount_current DECIMAL(14,2) GENERATED ALWAYS AS (quantity_current * unit_price) STORED,
    notes TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,
    deleted_by UUID REFERENCES auth.users(id),
    CONSTRAINT uq_est_conceptos_estimacion_concepto UNIQUE (estimacion_id, concepto_id)
);

-- Tabla: generadores (soporte de cantidades)
CREATE TABLE IF NOT EXISTS estimates.generadores (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    estimacion_concepto_id UUID NOT NULL REFERENCES estimates.estimacion_conceptos(id) ON DELETE CASCADE,
    generator_number VARCHAR(30) NOT NULL,
    description TEXT,
    status estimates.generator_status NOT NULL DEFAULT 'draft',
    lote_id UUID REFERENCES construction.lotes(id),
    departamento_id UUID REFERENCES construction.departamentos(id),
    location_description VARCHAR(255),
    quantity DECIMAL(12,4) NOT NULL DEFAULT 0,
    formula TEXT,
    photo_url VARCHAR(500),
    sketch_url VARCHAR(500),
    captured_by UUID NOT NULL REFERENCES auth.users(id),
    captured_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    approved_by UUID REFERENCES auth.users(id),
    approved_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,
    deleted_by UUID REFERENCES auth.users(id)
);

-- ============================================================================
-- TABLES - ANTICIPOS
-- ============================================================================

-- Tabla: anticipos (anticipos otorgados)
CREATE TABLE IF NOT EXISTS estimates.anticipos (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    contrato_id UUID NOT NULL REFERENCES construction.contratos(id),
    advance_type estimates.advance_type NOT NULL DEFAULT 'initial',
    advance_number VARCHAR(30) NOT NULL,
    advance_date DATE NOT NULL,
    gross_amount DECIMAL(16,2) NOT NULL,
    tax_amount DECIMAL(16,2) DEFAULT 0,
    net_amount DECIMAL(16,2) NOT NULL,
    amortization_percentage DECIMAL(5,2) DEFAULT 0,
    amortized_amount DECIMAL(16,2) DEFAULT 0,
    pending_amount DECIMAL(16,2) GENERATED ALWAYS AS (net_amount - amortized_amount) STORED,
    is_fully_amortized BOOLEAN DEFAULT FALSE,
    approved_at TIMESTAMPTZ,
    approved_by UUID REFERENCES auth.users(id),
    paid_at TIMESTAMPTZ,
    payment_reference VARCHAR(100),
    notes TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,
    deleted_by UUID REFERENCES auth.users(id),
    CONSTRAINT uq_anticipos_number_tenant UNIQUE (tenant_id, advance_number)
);

-- Tabla: amortizaciones (amortizaciones de anticipos)
CREATE TABLE IF NOT EXISTS estimates.amortizaciones (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    anticipo_id UUID NOT NULL REFERENCES estimates.anticipos(id),
    estimacion_id UUID NOT NULL REFERENCES estimates.estimaciones(id),
    amount DECIMAL(16,2) NOT NULL,
    amortization_date DATE NOT NULL,
    notes TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,
    deleted_by UUID REFERENCES auth.users(id),
    CONSTRAINT uq_amortizaciones_anticipo_estimacion UNIQUE (anticipo_id, estimacion_id)
);

-- ============================================================================
-- TABLES - RETENCIONES
-- ============================================================================

-- Tabla: retenciones (retenciones aplicadas)
CREATE TABLE IF NOT EXISTS estimates.retenciones (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    estimacion_id UUID NOT NULL REFERENCES estimates.estimaciones(id),
    retention_type estimates.retention_type NOT NULL,
    description VARCHAR(255) NOT NULL,
    percentage DECIMAL(5,2),
    amount DECIMAL(16,2) NOT NULL,
    release_date DATE,
    released_at TIMESTAMPTZ,
    released_amount DECIMAL(16,2),
    notes TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,
    deleted_by UUID REFERENCES auth.users(id)
);

-- Tabla: fondo_garantia (acumulado de fondo de garantía)
CREATE TABLE IF NOT EXISTS estimates.fondo_garantia (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    contrato_id UUID NOT NULL REFERENCES construction.contratos(id),
    accumulated_amount DECIMAL(16,2) DEFAULT 0,
    released_amount DECIMAL(16,2) DEFAULT 0,
    pending_amount DECIMAL(16,2) GENERATED ALWAYS AS (accumulated_amount - released_amount) STORED,
    release_date DATE,
    released_at TIMESTAMPTZ,
    released_by UUID REFERENCES auth.users(id),
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ,
    updated_by UUID REFERENCES auth.users(id),
    deleted_at TIMESTAMPTZ,
    deleted_by UUID REFERENCES auth.users(id),
    CONSTRAINT uq_fondo_garantia_contrato UNIQUE (contrato_id)
);

-- ============================================================================
-- TABLES - WORKFLOW
-- ============================================================================

-- Tabla: estimacion_workflow (historial de workflow)
CREATE TABLE IF NOT EXISTS estimates.estimacion_workflow (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    estimacion_id UUID NOT NULL REFERENCES estimates.estimaciones(id) ON DELETE CASCADE,
    from_status estimates.estimate_status,
    to_status estimates.estimate_status NOT NULL,
    action VARCHAR(50) NOT NULL,
    comments TEXT,
    performed_by UUID NOT NULL REFERENCES auth.users(id),
    performed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID REFERENCES auth.users(id)
);

-- ============================================================================
-- INDICES
-- ============================================================================

CREATE INDEX IF NOT EXISTS idx_estimaciones_tenant_id ON estimates.estimaciones(tenant_id);
CREATE INDEX IF NOT EXISTS idx_estimaciones_contrato_id ON estimates.estimaciones(contrato_id);
CREATE INDEX IF NOT EXISTS idx_estimaciones_fraccionamiento_id ON estimates.estimaciones(fraccionamiento_id);
CREATE INDEX IF NOT EXISTS idx_estimaciones_status ON estimates.estimaciones(status);
CREATE INDEX IF NOT EXISTS idx_estimaciones_period ON estimates.estimaciones(period_start, period_end);

CREATE INDEX IF NOT EXISTS idx_est_conceptos_tenant_id ON estimates.estimacion_conceptos(tenant_id);
CREATE INDEX IF NOT EXISTS idx_est_conceptos_estimacion_id ON estimates.estimacion_conceptos(estimacion_id);
CREATE INDEX IF NOT EXISTS idx_est_conceptos_concepto_id ON estimates.estimacion_conceptos(concepto_id);

CREATE INDEX IF NOT EXISTS idx_generadores_tenant_id ON estimates.generadores(tenant_id);
CREATE INDEX IF NOT EXISTS idx_generadores_est_concepto_id ON estimates.generadores(estimacion_concepto_id);
CREATE INDEX IF NOT EXISTS idx_generadores_status ON estimates.generadores(status);

CREATE INDEX IF NOT EXISTS idx_anticipos_tenant_id ON estimates.anticipos(tenant_id);
CREATE INDEX IF NOT EXISTS idx_anticipos_contrato_id ON estimates.anticipos(contrato_id);
CREATE INDEX IF NOT EXISTS idx_anticipos_type ON estimates.anticipos(advance_type);

CREATE INDEX IF NOT EXISTS idx_amortizaciones_tenant_id ON estimates.amortizaciones(tenant_id);
CREATE INDEX IF NOT EXISTS idx_amortizaciones_anticipo_id ON estimates.amortizaciones(anticipo_id);
CREATE INDEX IF NOT EXISTS idx_amortizaciones_estimacion_id ON estimates.amortizaciones(estimacion_id);

CREATE INDEX IF NOT EXISTS idx_retenciones_tenant_id ON estimates.retenciones(tenant_id);
CREATE INDEX IF NOT EXISTS idx_retenciones_estimacion_id ON estimates.retenciones(estimacion_id);
CREATE INDEX IF NOT EXISTS idx_retenciones_type ON estimates.retenciones(retention_type);

CREATE INDEX IF NOT EXISTS idx_fondo_garantia_tenant_id ON estimates.fondo_garantia(tenant_id);
CREATE INDEX IF NOT EXISTS idx_fondo_garantia_contrato_id ON estimates.fondo_garantia(contrato_id);

CREATE INDEX IF NOT EXISTS idx_est_workflow_tenant_id ON estimates.estimacion_workflow(tenant_id);
CREATE INDEX IF NOT EXISTS idx_est_workflow_estimacion_id ON estimates.estimacion_workflow(estimacion_id);

-- ============================================================================
-- ROW LEVEL SECURITY (RLS)
-- ============================================================================

ALTER TABLE estimates.estimaciones ENABLE ROW LEVEL SECURITY;
ALTER TABLE estimates.estimacion_conceptos ENABLE ROW LEVEL SECURITY;
ALTER TABLE estimates.generadores ENABLE ROW LEVEL SECURITY;
ALTER TABLE estimates.anticipos ENABLE ROW LEVEL SECURITY;
ALTER TABLE estimates.amortizaciones ENABLE ROW LEVEL SECURITY;
ALTER TABLE estimates.retenciones ENABLE ROW LEVEL SECURITY;
ALTER TABLE estimates.fondo_garantia ENABLE ROW LEVEL SECURITY;
ALTER TABLE estimates.estimacion_workflow ENABLE ROW LEVEL SECURITY;

DO $$ BEGIN
    DROP POLICY IF EXISTS tenant_isolation_estimaciones ON estimates.estimaciones;
    CREATE POLICY tenant_isolation_estimaciones ON estimates.estimaciones
        FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
EXCEPTION WHEN undefined_object THEN NULL; END $$;

DO $$ BEGIN
    DROP POLICY IF EXISTS tenant_isolation_est_conceptos ON estimates.estimacion_conceptos;
    CREATE POLICY tenant_isolation_est_conceptos ON estimates.estimacion_conceptos
        FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
EXCEPTION WHEN undefined_object THEN NULL; END $$;

DO $$ BEGIN
    DROP POLICY IF EXISTS tenant_isolation_generadores ON estimates.generadores;
    CREATE POLICY tenant_isolation_generadores ON estimates.generadores
        FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
EXCEPTION WHEN undefined_object THEN NULL; END $$;

DO $$ BEGIN
    DROP POLICY IF EXISTS tenant_isolation_anticipos ON estimates.anticipos;
    CREATE POLICY tenant_isolation_anticipos ON estimates.anticipos
        FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
EXCEPTION WHEN undefined_object THEN NULL; END $$;

DO $$ BEGIN
    DROP POLICY IF EXISTS tenant_isolation_amortizaciones ON estimates.amortizaciones;
    CREATE POLICY tenant_isolation_amortizaciones ON estimates.amortizaciones
        FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
EXCEPTION WHEN undefined_object THEN NULL; END $$;

DO $$ BEGIN
    DROP POLICY IF EXISTS tenant_isolation_retenciones ON estimates.retenciones;
    CREATE POLICY tenant_isolation_retenciones ON estimates.retenciones
        FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
EXCEPTION WHEN undefined_object THEN NULL; END $$;

DO $$ BEGIN
    DROP POLICY IF EXISTS tenant_isolation_fondo_garantia ON estimates.fondo_garantia;
    CREATE POLICY tenant_isolation_fondo_garantia ON estimates.fondo_garantia
        FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
EXCEPTION WHEN undefined_object THEN NULL; END $$;

DO $$ BEGIN
    DROP POLICY IF EXISTS tenant_isolation_est_workflow ON estimates.estimacion_workflow;
    CREATE POLICY tenant_isolation_est_workflow ON estimates.estimacion_workflow
        FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
EXCEPTION WHEN undefined_object THEN NULL; END $$;

-- ============================================================================
-- FUNCIONES
-- ============================================================================

-- Función: calcular totales de estimación
CREATE OR REPLACE FUNCTION estimates.calculate_estimate_totals(p_estimacion_id UUID)
RETURNS VOID AS $$
DECLARE
    v_subtotal DECIMAL(16,2);
    v_advance DECIMAL(16,2);
    v_retention DECIMAL(16,2);
    v_tax_rate DECIMAL(5,2) := 0.16;
    v_tax DECIMAL(16,2);
    v_total DECIMAL(16,2);
BEGIN
    SELECT COALESCE(SUM(amount_current), 0) INTO v_subtotal
    FROM estimates.estimacion_conceptos
    WHERE estimacion_id = p_estimacion_id AND deleted_at IS NULL;

    SELECT COALESCE(SUM(amount), 0) INTO v_advance
    FROM estimates.amortizaciones
    WHERE estimacion_id = p_estimacion_id AND deleted_at IS NULL;

    SELECT COALESCE(SUM(amount), 0) INTO v_retention
    FROM estimates.retenciones
    WHERE estimacion_id = p_estimacion_id AND deleted_at IS NULL;

    v_tax := v_subtotal * v_tax_rate;
    v_total := v_subtotal + v_tax - v_advance - v_retention;

    UPDATE estimates.estimaciones
    SET subtotal = v_subtotal,
        advance_amount = v_advance,
        retention_amount = v_retention,
        tax_amount = v_tax,
        total_amount = v_total,
        updated_at = NOW()
    WHERE id = p_estimacion_id;
END;
$$ LANGUAGE plpgsql;

-- ============================================================================
-- COMENTARIOS
-- ============================================================================

COMMENT ON SCHEMA estimates IS 'Schema de estimaciones, anticipos y retenciones de obra';
COMMENT ON TABLE estimates.estimaciones IS 'Estimaciones de obra periódicas';
COMMENT ON TABLE estimates.estimacion_conceptos IS 'Líneas de concepto por estimación';
COMMENT ON TABLE estimates.generadores IS 'Generadores de cantidades para estimaciones';
COMMENT ON TABLE estimates.anticipos IS 'Anticipos otorgados a subcontratistas';
COMMENT ON TABLE estimates.amortizaciones IS 'Amortizaciones de anticipos por estimación';
COMMENT ON TABLE estimates.retenciones IS 'Retenciones aplicadas a estimaciones';
COMMENT ON TABLE estimates.fondo_garantia IS 'Fondo de garantía acumulado por contrato';
COMMENT ON TABLE estimates.estimacion_workflow IS 'Historial de workflow de estimaciones';

-- ============================================================================
-- FIN DEL SCHEMA ESTIMATES
-- Total tablas: 8
-- ============================================================================
