-- ============================================================================
-- MIGRACIÓN: Sistema de Reportes Financieros
-- Fecha: 2025-12-12
-- Descripción: Crea tablas para definición, ejecución y programación de reportes
-- Impacto: Módulo financiero y verticales que requieren reportes contables
-- Rollback: DROP TABLE y DROP FUNCTION incluidos al final
-- ============================================================================

-- ============================================================================
-- 1. TABLA DE DEFINICIONES DE REPORTES
-- ============================================================================

CREATE TABLE IF NOT EXISTS reports.report_definitions (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  -- Identificación
  code VARCHAR(50) NOT NULL,
  name VARCHAR(255) NOT NULL,
  description TEXT,

  -- Clasificación
  report_type VARCHAR(50) NOT NULL DEFAULT 'financial',
  -- financial, accounting, tax, management, custom
  category VARCHAR(100),
  -- balance_sheet, income_statement, cash_flow, trial_balance, ledger, etc.

  -- Configuración de consulta
  base_query TEXT,  -- SQL base o referencia a función
  query_function VARCHAR(255),  -- Nombre de función PostgreSQL si usa función

  -- Parámetros requeridos (JSON Schema)
  parameters_schema JSONB DEFAULT '{}',
  -- Ejemplo: {"date_from": {"type": "date", "required": true}, "company_id": {"type": "uuid"}}

  -- Configuración de columnas
  columns_config JSONB DEFAULT '[]',
  -- Ejemplo: [{"name": "account", "label": "Cuenta", "type": "string"}, {"name": "debit", "label": "Debe", "type": "currency"}]

  -- Agrupaciones disponibles
  grouping_options JSONB DEFAULT '[]',
  -- Ejemplo: ["account_type", "company", "period"]

  -- Configuración de totales
  totals_config JSONB DEFAULT '{}',
  -- Ejemplo: {"show_totals": true, "total_columns": ["debit", "credit", "balance"]}

  -- Plantillas de exportación
  export_formats JSONB DEFAULT '["pdf", "xlsx", "csv"]',
  pdf_template VARCHAR(255),  -- Referencia a plantilla PDF
  xlsx_template VARCHAR(255),

  -- Estado y visibilidad
  is_system BOOLEAN DEFAULT false,  -- Reportes del sistema vs personalizados
  is_active BOOLEAN DEFAULT true,

  -- Permisos requeridos
  required_permissions JSONB DEFAULT '[]',
  -- Ejemplo: ["financial.reports.view", "financial.reports.balance_sheet"]

  -- Metadata
  version INTEGER DEFAULT 1,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  updated_by UUID REFERENCES auth.users(id),

  -- Constraints
  UNIQUE(tenant_id, code)
);

COMMENT ON TABLE reports.report_definitions IS
'Definiciones de reportes disponibles en el sistema. Incluye reportes predefinidos y personalizados.';

-- ============================================================================
-- 2. TABLA DE EJECUCIONES DE REPORTES
-- ============================================================================

CREATE TABLE IF NOT EXISTS reports.report_executions (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  definition_id UUID NOT NULL REFERENCES reports.report_definitions(id) ON DELETE CASCADE,

  -- Parámetros de ejecución
  parameters JSONB NOT NULL DEFAULT '{}',
  -- Los valores específicos usados para esta ejecución

  -- Estado de ejecución
  status VARCHAR(20) NOT NULL DEFAULT 'pending',
  -- pending, running, completed, failed, cancelled

  -- Tiempos
  started_at TIMESTAMPTZ,
  completed_at TIMESTAMPTZ,
  execution_time_ms INTEGER,

  -- Resultados
  row_count INTEGER,
  result_data JSONB,  -- Datos del reporte (puede ser grande)
  result_summary JSONB,  -- Resumen/totales

  -- Archivos generados
  output_files JSONB DEFAULT '[]',
  -- Ejemplo: [{"format": "pdf", "path": "/reports/...", "size": 12345}]

  -- Errores
  error_message TEXT,
  error_details JSONB,

  -- Metadata
  requested_by UUID NOT NULL REFERENCES auth.users(id),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

COMMENT ON TABLE reports.report_executions IS
'Historial de ejecuciones de reportes con sus resultados y archivos generados.';

-- ============================================================================
-- 3. TABLA DE PROGRAMACIÓN DE REPORTES
-- ============================================================================

CREATE TABLE IF NOT EXISTS reports.report_schedules (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  definition_id UUID NOT NULL REFERENCES reports.report_definitions(id) ON DELETE CASCADE,
  company_id UUID REFERENCES auth.companies(id) ON DELETE CASCADE,

  -- Nombre del schedule
  name VARCHAR(255) NOT NULL,

  -- Parámetros predeterminados
  default_parameters JSONB DEFAULT '{}',

  -- Programación (cron expression)
  cron_expression VARCHAR(100) NOT NULL,
  -- Ejemplo: "0 8 1 * *" (primer día del mes a las 8am)

  timezone VARCHAR(50) DEFAULT 'America/Mexico_City',

  -- Estado
  is_active BOOLEAN DEFAULT true,

  -- Última ejecución
  last_execution_id UUID REFERENCES reports.report_executions(id),
  last_run_at TIMESTAMPTZ,
  next_run_at TIMESTAMPTZ,

  -- Destino de entrega
  delivery_method VARCHAR(50) DEFAULT 'none',
  -- none, email, storage, webhook
  delivery_config JSONB DEFAULT '{}',
  -- Para email: {"recipients": ["a@b.com"], "subject": "...", "format": "pdf"}
  -- Para storage: {"path": "/reports/scheduled/", "retention_days": 30}

  -- Metadata
  created_at TIMESTAMPTZ DEFAULT NOW(),
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  updated_by UUID REFERENCES auth.users(id)
);

COMMENT ON TABLE reports.report_schedules IS
'Programación automática de reportes con opciones de entrega.';

-- ============================================================================
-- 4. TABLA DE PLANTILLAS DE REPORTES
-- ============================================================================

CREATE TABLE IF NOT EXISTS reports.report_templates (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  -- Identificación
  code VARCHAR(50) NOT NULL,
  name VARCHAR(255) NOT NULL,

  -- Tipo de plantilla
  template_type VARCHAR(20) NOT NULL,
  -- pdf, xlsx, html

  -- Contenido de la plantilla
  template_content BYTEA,  -- Para plantillas binarias (XLSX)
  template_html TEXT,  -- Para plantillas HTML/PDF

  -- Estilos CSS (para PDF/HTML)
  styles TEXT,

  -- Variables disponibles
  available_variables JSONB DEFAULT '[]',

  -- Estado
  is_active BOOLEAN DEFAULT true,

  -- Metadata
  created_at TIMESTAMPTZ DEFAULT NOW(),
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMPTZ DEFAULT NOW(),

  UNIQUE(tenant_id, code)
);

COMMENT ON TABLE reports.report_templates IS
'Plantillas personalizables para la generación de reportes en diferentes formatos.';

-- ============================================================================
-- 5. ÍNDICES
-- ============================================================================

CREATE INDEX IF NOT EXISTS idx_report_definitions_tenant_type
  ON reports.report_definitions(tenant_id, report_type);

CREATE INDEX IF NOT EXISTS idx_report_definitions_tenant_category
  ON reports.report_definitions(tenant_id, category);

CREATE INDEX IF NOT EXISTS idx_report_executions_tenant_status
  ON reports.report_executions(tenant_id, status);

CREATE INDEX IF NOT EXISTS idx_report_executions_definition
  ON reports.report_executions(definition_id);

CREATE INDEX IF NOT EXISTS idx_report_executions_created
  ON reports.report_executions(tenant_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_report_schedules_next_run
  ON reports.report_schedules(next_run_at)
  WHERE is_active = true;

-- ============================================================================
-- 6. RLS (Row Level Security)
-- ============================================================================

ALTER TABLE reports.report_definitions ENABLE ROW LEVEL SECURITY;
ALTER TABLE reports.report_executions ENABLE ROW LEVEL SECURITY;
ALTER TABLE reports.report_schedules ENABLE ROW LEVEL SECURITY;
ALTER TABLE reports.report_templates ENABLE ROW LEVEL SECURITY;

-- Políticas para report_definitions
DROP POLICY IF EXISTS report_definitions_tenant_isolation ON reports.report_definitions;
CREATE POLICY report_definitions_tenant_isolation ON reports.report_definitions
  USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Políticas para report_executions
DROP POLICY IF EXISTS report_executions_tenant_isolation ON reports.report_executions;
CREATE POLICY report_executions_tenant_isolation ON reports.report_executions
  USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Políticas para report_schedules
DROP POLICY IF EXISTS report_schedules_tenant_isolation ON reports.report_schedules;
CREATE POLICY report_schedules_tenant_isolation ON reports.report_schedules
  USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Políticas para report_templates
DROP POLICY IF EXISTS report_templates_tenant_isolation ON reports.report_templates;
CREATE POLICY report_templates_tenant_isolation ON reports.report_templates
  USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- ============================================================================
-- 7. FUNCIONES DE REPORTES PREDEFINIDOS
-- ============================================================================

-- Balance de Comprobación
CREATE OR REPLACE FUNCTION reports.generate_trial_balance(
  p_tenant_id UUID,
  p_company_id UUID,
  p_date_from DATE,
  p_date_to DATE,
  p_include_zero_balance BOOLEAN DEFAULT false
)
RETURNS TABLE (
  account_id UUID,
  account_code VARCHAR(20),
  account_name VARCHAR(255),
  account_type VARCHAR(50),
  initial_debit DECIMAL(16,2),
  initial_credit DECIMAL(16,2),
  period_debit DECIMAL(16,2),
  period_credit DECIMAL(16,2),
  final_debit DECIMAL(16,2),
  final_credit DECIMAL(16,2)
) AS $$
BEGIN
  RETURN QUERY
  WITH account_balances AS (
    -- Saldos iniciales (antes del período)
    SELECT
      a.id as account_id,
      a.code as account_code,
      a.name as account_name,
      a.account_type,
      COALESCE(SUM(CASE WHEN jel.transaction_date < p_date_from THEN jel.debit ELSE 0 END), 0) as initial_debit,
      COALESCE(SUM(CASE WHEN jel.transaction_date < p_date_from THEN jel.credit ELSE 0 END), 0) as initial_credit,
      COALESCE(SUM(CASE WHEN jel.transaction_date BETWEEN p_date_from AND p_date_to THEN jel.debit ELSE 0 END), 0) as period_debit,
      COALESCE(SUM(CASE WHEN jel.transaction_date BETWEEN p_date_from AND p_date_to THEN jel.credit ELSE 0 END), 0) as period_credit
    FROM financial.accounts a
    LEFT JOIN financial.journal_entry_lines jel ON a.id = jel.account_id
    LEFT JOIN financial.journal_entries je ON jel.journal_entry_id = je.id AND je.status = 'posted'
    WHERE a.tenant_id = p_tenant_id
      AND (p_company_id IS NULL OR a.company_id = p_company_id)
      AND a.is_active = true
    GROUP BY a.id, a.code, a.name, a.account_type
  )
  SELECT
    ab.account_id,
    ab.account_code,
    ab.account_name,
    ab.account_type,
    ab.initial_debit,
    ab.initial_credit,
    ab.period_debit,
    ab.period_credit,
    ab.initial_debit + ab.period_debit as final_debit,
    ab.initial_credit + ab.period_credit as final_credit
  FROM account_balances ab
  WHERE p_include_zero_balance = true
     OR (ab.initial_debit + ab.period_debit) != 0
     OR (ab.initial_credit + ab.period_credit) != 0
  ORDER BY ab.account_code;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION reports.generate_trial_balance IS
'Genera el balance de comprobación para un período específico.';

-- Libro Mayor
CREATE OR REPLACE FUNCTION reports.generate_general_ledger(
  p_tenant_id UUID,
  p_company_id UUID,
  p_account_id UUID,
  p_date_from DATE,
  p_date_to DATE
)
RETURNS TABLE (
  entry_date DATE,
  journal_entry_id UUID,
  entry_number VARCHAR(50),
  description TEXT,
  partner_name VARCHAR(255),
  debit DECIMAL(16,2),
  credit DECIMAL(16,2),
  running_balance DECIMAL(16,2)
) AS $$
BEGIN
  RETURN QUERY
  WITH movements AS (
    SELECT
      je.entry_date,
      je.id as journal_entry_id,
      je.entry_number,
      je.description,
      p.name as partner_name,
      jel.debit,
      jel.credit,
      ROW_NUMBER() OVER (ORDER BY je.entry_date, je.id) as rn
    FROM financial.journal_entry_lines jel
    JOIN financial.journal_entries je ON jel.journal_entry_id = je.id
    LEFT JOIN core.partners p ON je.partner_id = p.id
    WHERE jel.account_id = p_account_id
      AND jel.tenant_id = p_tenant_id
      AND je.status = 'posted'
      AND je.entry_date BETWEEN p_date_from AND p_date_to
      AND (p_company_id IS NULL OR je.company_id = p_company_id)
    ORDER BY je.entry_date, je.id
  )
  SELECT
    m.entry_date,
    m.journal_entry_id,
    m.entry_number,
    m.description,
    m.partner_name,
    m.debit,
    m.credit,
    SUM(m.debit - m.credit) OVER (ORDER BY m.rn) as running_balance
  FROM movements m;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION reports.generate_general_ledger IS
'Genera el libro mayor para una cuenta específica.';

-- ============================================================================
-- 8. DATOS SEMILLA: REPORTES PREDEFINIDOS DEL SISTEMA
-- ============================================================================

-- Nota: Los reportes del sistema se insertan con is_system = true
-- y se insertan solo si no existen (usando ON CONFLICT)

DO $$
DECLARE
  v_system_tenant_id UUID;
BEGIN
  -- Obtener el tenant del sistema (si existe)
  SELECT id INTO v_system_tenant_id
  FROM auth.tenants
  WHERE code = 'system' OR is_system = true
  LIMIT 1;

  -- Solo insertar si hay un tenant sistema
  IF v_system_tenant_id IS NOT NULL THEN
    -- Balance de Comprobación
    INSERT INTO reports.report_definitions (
      tenant_id, code, name, description, report_type, category,
      query_function, parameters_schema, columns_config, is_system
    ) VALUES (
      v_system_tenant_id,
      'TRIAL_BALANCE',
      'Balance de Comprobación',
      'Reporte de balance de comprobación con saldos iniciales, movimientos y saldos finales',
      'financial',
      'trial_balance',
      'reports.generate_trial_balance',
      '{"company_id": {"type": "uuid", "required": false}, "date_from": {"type": "date", "required": true}, "date_to": {"type": "date", "required": true}, "include_zero": {"type": "boolean", "default": false}}',
      '[{"name": "account_code", "label": "Código", "type": "string"}, {"name": "account_name", "label": "Cuenta", "type": "string"}, {"name": "initial_debit", "label": "Debe Inicial", "type": "currency"}, {"name": "initial_credit", "label": "Haber Inicial", "type": "currency"}, {"name": "period_debit", "label": "Debe Período", "type": "currency"}, {"name": "period_credit", "label": "Haber Período", "type": "currency"}, {"name": "final_debit", "label": "Debe Final", "type": "currency"}, {"name": "final_credit", "label": "Haber Final", "type": "currency"}]',
      true
    ) ON CONFLICT (tenant_id, code) DO NOTHING;

    -- Libro Mayor
    INSERT INTO reports.report_definitions (
      tenant_id, code, name, description, report_type, category,
      query_function, parameters_schema, columns_config, is_system
    ) VALUES (
      v_system_tenant_id,
      'GENERAL_LEDGER',
      'Libro Mayor',
      'Detalle de movimientos por cuenta con saldo acumulado',
      'financial',
      'ledger',
      'reports.generate_general_ledger',
      '{"company_id": {"type": "uuid", "required": false}, "account_id": {"type": "uuid", "required": true}, "date_from": {"type": "date", "required": true}, "date_to": {"type": "date", "required": true}}',
      '[{"name": "entry_date", "label": "Fecha", "type": "date"}, {"name": "entry_number", "label": "Número", "type": "string"}, {"name": "description", "label": "Descripción", "type": "string"}, {"name": "partner_name", "label": "Tercero", "type": "string"}, {"name": "debit", "label": "Debe", "type": "currency"}, {"name": "credit", "label": "Haber", "type": "currency"}, {"name": "running_balance", "label": "Saldo", "type": "currency"}]',
      true
    ) ON CONFLICT (tenant_id, code) DO NOTHING;

    RAISE NOTICE 'Reportes del sistema insertados correctamente';
  END IF;
END $$;

-- ============================================================================
-- ROLLBACK SCRIPT
-- ============================================================================
/*
DROP FUNCTION IF EXISTS reports.generate_general_ledger(UUID, UUID, UUID, DATE, DATE);
DROP FUNCTION IF EXISTS reports.generate_trial_balance(UUID, UUID, DATE, DATE, BOOLEAN);
DROP TABLE IF EXISTS reports.report_templates;
DROP TABLE IF EXISTS reports.report_schedules;
DROP TABLE IF EXISTS reports.report_executions;
DROP TABLE IF EXISTS reports.report_definitions;
*/

-- ============================================================================
-- VERIFICACIÓN
-- ============================================================================

DO $$
BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'reports' AND tablename = 'report_definitions') THEN
    RAISE EXCEPTION 'Error: Tabla report_definitions no fue creada';
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'reports' AND tablename = 'report_executions') THEN
    RAISE EXCEPTION 'Error: Tabla report_executions no fue creada';
  END IF;

  RAISE NOTICE 'Migración completada exitosamente: Reportes Financieros';
END $$;
