-- =====================================================
-- SCHEMA: estimates
-- PROPOSITO: Estimaciones, anticipos, retenciones, generadores
-- MODULOS: MAI-008 (Estimaciones y Facturacion)
-- FECHA: 2025-11-24
-- ESPECIFICO: 100% construccion
-- =====================================================

-- Crear schema
CREATE SCHEMA IF NOT EXISTS estimates;

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

CREATE TYPE estimates.estimate_status AS ENUM (
  'draft',           -- Borrador
  'submitted',       -- Enviada para revision
  'reviewed',        -- Revisada
  'approved',        -- Aprobada
  'invoiced',        -- Facturada
  'paid',            -- Pagada
  'rejected',        -- Rechazada
  'cancelled'        -- Cancelada
);

CREATE TYPE estimates.advance_type AS ENUM (
  'initial',         -- Anticipo inicial
  'progress',        -- Anticipo por avance
  'materials'        -- Anticipo para materiales
);

CREATE TYPE estimates.retention_type AS ENUM (
  'guarantee',       -- Retencion de garantia (fondo)
  'tax',             -- Retencion fiscal
  'penalty',         -- Penalizacion
  'other'            -- Otra retencion
);

CREATE TYPE estimates.generator_status AS ENUM (
  'draft',           -- Borrador
  'in_progress',     -- En captura
  'completed',       -- Completado
  'approved'         -- Aprobado
);

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

-- Tabla: estimaciones (estimaciones de obra)
CREATE TABLE 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, -- Numero de estimacion (1, 2, 3...)
  status estimates.estimate_status NOT NULL DEFAULT 'draft',

  -- Montos
  subtotal DECIMAL(16,2) DEFAULT 0,
  advance_amount DECIMAL(16,2) DEFAULT 0, -- Amortizacion de anticipo
  retention_amount DECIMAL(16,2) DEFAULT 0, -- Retencion
  tax_amount DECIMAL(16,2) DEFAULT 0, -- IVA
  total_amount DECIMAL(16,2) DEFAULT 0, -- Neto a pagar

  -- Aprobaciones
  submitted_at TIMESTAMP,
  submitted_by UUID REFERENCES auth.users(id),
  reviewed_at TIMESTAMP,
  reviewed_by UUID REFERENCES auth.users(id),
  approved_at TIMESTAMP,
  approved_by UUID REFERENCES auth.users(id),

  -- Facturacion
  invoice_id UUID, -- FK a facturas (financial.invoices)
  invoiced_at TIMESTAMP,
  paid_at TIMESTAMP,

  notes TEXT,

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  deleted_at TIMESTAMP,
  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 (lineas de estimacion)
CREATE TABLE 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),

  -- Cantidades
  quantity_contract DECIMAL(12,4) DEFAULT 0, -- Cantidad contratada
  quantity_previous DECIMAL(12,4) DEFAULT 0, -- Acumulado anterior
  quantity_current DECIMAL(12,4) DEFAULT 0, -- Esta estimacion
  quantity_accumulated DECIMAL(12,4) GENERATED ALWAYS AS (quantity_previous + quantity_current) STORED,

  -- Precios
  unit_price DECIMAL(12,4) NOT NULL DEFAULT 0,
  amount_current DECIMAL(14,2) GENERATED ALWAYS AS (quantity_current * unit_price) STORED,

  notes TEXT,

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  deleted_at TIMESTAMP,
  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 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',

  -- Ubicacion
  lote_id UUID REFERENCES construction.lotes(id),
  departamento_id UUID REFERENCES construction.departamentos(id),
  location_description VARCHAR(255), -- Descripcion libre de ubicacion

  -- Calculo
  quantity DECIMAL(12,4) NOT NULL DEFAULT 0,
  formula TEXT, -- Formula de calculo (ej: "3.5 x 2.0 x 0.15")

  -- Evidencia
  photo_url VARCHAR(500),
  sketch_url VARCHAR(500),

  captured_by UUID NOT NULL REFERENCES auth.users(id),
  captured_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  approved_by UUID REFERENCES auth.users(id),
  approved_at TIMESTAMP,

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  deleted_at TIMESTAMP,
  deleted_by UUID REFERENCES auth.users(id)
);

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

-- Tabla: anticipos (anticipos otorgados)
CREATE TABLE 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,

  -- Montos
  gross_amount DECIMAL(16,2) NOT NULL, -- Monto bruto
  tax_amount DECIMAL(16,2) DEFAULT 0, -- IVA
  net_amount DECIMAL(16,2) NOT NULL, -- Monto neto

  -- Amortizacion
  amortization_percentage DECIMAL(5,2) DEFAULT 0, -- % a amortizar por estimacion
  amortized_amount DECIMAL(16,2) DEFAULT 0, -- Total amortizado
  pending_amount DECIMAL(16,2) GENERATED ALWAYS AS (net_amount - amortized_amount) STORED,
  is_fully_amortized BOOLEAN DEFAULT FALSE,

  -- Aprobacion
  approved_at TIMESTAMP,
  approved_by UUID REFERENCES auth.users(id),

  -- Pago
  paid_at TIMESTAMP,
  payment_reference VARCHAR(100),

  notes TEXT,

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  deleted_at TIMESTAMP,
  deleted_by UUID REFERENCES auth.users(id),

  CONSTRAINT uq_anticipos_number_tenant UNIQUE (tenant_id, advance_number)
);

-- Tabla: amortizaciones (amortizaciones de anticipos)
CREATE TABLE 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,

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  deleted_at TIMESTAMP,
  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 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), -- Porcentaje de retencion
  amount DECIMAL(16,2) NOT NULL, -- Monto retenido
  release_date DATE, -- Fecha de liberacion programada
  released_at TIMESTAMP, -- Fecha de liberacion real
  released_amount DECIMAL(16,2), -- Monto liberado
  notes TEXT,

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  deleted_at TIMESTAMP,
  deleted_by UUID REFERENCES auth.users(id)
);

-- Tabla: fondo_garantia (acumulado de fondo de garantia)
CREATE TABLE 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, -- Total acumulado
  released_amount DECIMAL(16,2) DEFAULT 0, -- Total liberado
  pending_amount DECIMAL(16,2) GENERATED ALWAYS AS (accumulated_amount - released_amount) STORED,

  -- Liberacion
  release_date DATE, -- Fecha programada de liberacion
  released_at TIMESTAMP,
  released_by UUID REFERENCES auth.users(id),

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  deleted_at TIMESTAMP,
  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 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, -- submit, review, approve, reject, etc.
  comments TEXT,
  performed_by UUID NOT NULL REFERENCES auth.users(id),
  performed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  -- Auditoria
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id)
);

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

-- Estimaciones
CREATE INDEX idx_estimaciones_tenant_id ON estimates.estimaciones(tenant_id);
CREATE INDEX idx_estimaciones_contrato_id ON estimates.estimaciones(contrato_id);
CREATE INDEX idx_estimaciones_fraccionamiento_id ON estimates.estimaciones(fraccionamiento_id);
CREATE INDEX idx_estimaciones_status ON estimates.estimaciones(status);
CREATE INDEX idx_estimaciones_period ON estimates.estimaciones(period_start, period_end);
CREATE INDEX idx_estimaciones_active ON estimates.estimaciones(tenant_id) WHERE deleted_at IS NULL;

-- Estimacion Conceptos
CREATE INDEX idx_est_conceptos_tenant_id ON estimates.estimacion_conceptos(tenant_id);
CREATE INDEX idx_est_conceptos_estimacion_id ON estimates.estimacion_conceptos(estimacion_id);
CREATE INDEX idx_est_conceptos_concepto_id ON estimates.estimacion_conceptos(concepto_id);

-- Generadores
CREATE INDEX idx_generadores_tenant_id ON estimates.generadores(tenant_id);
CREATE INDEX idx_generadores_est_concepto_id ON estimates.generadores(estimacion_concepto_id);
CREATE INDEX idx_generadores_lote_id ON estimates.generadores(lote_id);
CREATE INDEX idx_generadores_status ON estimates.generadores(status);

-- Anticipos
CREATE INDEX idx_anticipos_tenant_id ON estimates.anticipos(tenant_id);
CREATE INDEX idx_anticipos_contrato_id ON estimates.anticipos(contrato_id);
CREATE INDEX idx_anticipos_type ON estimates.anticipos(advance_type);
CREATE INDEX idx_anticipos_pending ON estimates.anticipos(tenant_id) WHERE is_fully_amortized = FALSE;

-- Amortizaciones
CREATE INDEX idx_amortizaciones_tenant_id ON estimates.amortizaciones(tenant_id);
CREATE INDEX idx_amortizaciones_anticipo_id ON estimates.amortizaciones(anticipo_id);
CREATE INDEX idx_amortizaciones_estimacion_id ON estimates.amortizaciones(estimacion_id);

-- Retenciones
CREATE INDEX idx_retenciones_tenant_id ON estimates.retenciones(tenant_id);
CREATE INDEX idx_retenciones_estimacion_id ON estimates.retenciones(estimacion_id);
CREATE INDEX idx_retenciones_type ON estimates.retenciones(retention_type);
CREATE INDEX idx_retenciones_pending ON estimates.retenciones(tenant_id) WHERE released_at IS NULL;

-- Fondo Garantia
CREATE INDEX idx_fondo_garantia_tenant_id ON estimates.fondo_garantia(tenant_id);
CREATE INDEX idx_fondo_garantia_contrato_id ON estimates.fondo_garantia(contrato_id);

-- Workflow
CREATE INDEX idx_est_workflow_tenant_id ON estimates.estimacion_workflow(tenant_id);
CREATE INDEX idx_est_workflow_estimacion_id ON estimates.estimacion_workflow(estimacion_id);
CREATE INDEX idx_est_workflow_performed_at ON estimates.estimacion_workflow(performed_at);

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

-- Habilitar 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;

-- Policies
CREATE POLICY tenant_isolation_estimaciones ON estimates.estimaciones
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_est_conceptos ON estimates.estimacion_conceptos
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_generadores ON estimates.generadores
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_anticipos ON estimates.anticipos
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_amortizaciones ON estimates.amortizaciones
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_retenciones ON estimates.retenciones
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_fondo_garantia ON estimates.fondo_garantia
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_est_workflow ON estimates.estimacion_workflow
  USING (tenant_id = get_current_tenant_id());

-- =====================================================
-- FUNCTIONS
-- =====================================================

-- Funcion: calcular totales de estimacion
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; -- IVA Mexico
  v_tax DECIMAL(16,2);
  v_total DECIMAL(16,2);
BEGIN
  -- Calcular subtotal
  SELECT COALESCE(SUM(amount_current), 0) INTO v_subtotal
  FROM estimates.estimacion_conceptos
  WHERE estimacion_id = p_estimacion_id AND deleted_at IS NULL;

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

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

  -- Calcular IVA sobre subtotal
  v_tax := v_subtotal * v_tax_rate;

  -- Calcular total neto
  v_total := v_subtotal + v_tax - v_advance - v_retention;

  -- Actualizar estimacion
  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 = CURRENT_TIMESTAMP
  WHERE id = p_estimacion_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION estimates.calculate_estimate_totals IS 'Recalcula los totales de una estimacion';

-- Funcion: actualizar amortizacion de anticipo
CREATE OR REPLACE FUNCTION estimates.update_advance_amortization()
RETURNS TRIGGER AS $$
BEGIN
  IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
    UPDATE estimates.anticipos
    SET
      amortized_amount = (
        SELECT COALESCE(SUM(amount), 0)
        FROM estimates.amortizaciones
        WHERE anticipo_id = NEW.anticipo_id AND deleted_at IS NULL
      ),
      is_fully_amortized = (
        SELECT COALESCE(SUM(amount), 0) >= net_amount
        FROM estimates.amortizaciones
        WHERE anticipo_id = NEW.anticipo_id AND deleted_at IS NULL
      ),
      updated_at = CURRENT_TIMESTAMP
    WHERE id = NEW.anticipo_id;
  END IF;

  IF TG_OP = 'DELETE' THEN
    UPDATE estimates.anticipos
    SET
      amortized_amount = (
        SELECT COALESCE(SUM(amount), 0)
        FROM estimates.amortizaciones
        WHERE anticipo_id = OLD.anticipo_id AND deleted_at IS NULL
      ),
      is_fully_amortized = FALSE,
      updated_at = CURRENT_TIMESTAMP
    WHERE id = OLD.anticipo_id;
  END IF;

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

CREATE TRIGGER trg_update_advance_amortization
  AFTER INSERT OR UPDATE OR DELETE ON estimates.amortizaciones
  FOR EACH ROW
  EXECUTE FUNCTION estimates.update_advance_amortization();

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

COMMENT ON SCHEMA estimates IS 'Schema de estimaciones, anticipos y retenciones de obra';
COMMENT ON TABLE estimates.estimaciones IS 'Estimaciones de obra periodicas';
COMMENT ON TABLE estimates.estimacion_conceptos IS 'Lineas de concepto por estimacion';
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 estimacion';
COMMENT ON TABLE estimates.retenciones IS 'Retenciones aplicadas a estimaciones';
COMMENT ON TABLE estimates.fondo_garantia IS 'Fondo de garantia acumulado por contrato';
COMMENT ON TABLE estimates.estimacion_workflow IS 'Historial de workflow de estimaciones';

-- =====================================================
-- FIN DEL SCHEMA ESTIMATES
-- =====================================================
