-- =============================================================================
-- ERP TRANSPORTISTAS - Schema Despacho DDL
-- =============================================================================
-- Archivo: 09-dispatch-schema-ddl.sql
-- Version: 1.0.0
-- Fecha: 2026-01-28
-- Descripcion: Tablas para modulo de despacho (dispatch center)
-- Basado en: erp-mecanicas-diesel MMD-011 Dispatch Center
-- =============================================================================

-- =============================================================================
-- SCHEMA DESPACHO
-- =============================================================================

CREATE SCHEMA IF NOT EXISTS despacho;

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

DO $$ BEGIN
  CREATE TYPE despacho.estado_unidad AS ENUM (
    'available',
    'assigned',
    'en_route',
    'on_site',
    'returning',
    'offline',
    'maintenance'
  );
EXCEPTION WHEN duplicate_object THEN null;
END $$;

DO $$ BEGIN
  CREATE TYPE despacho.capacidad_unidad AS ENUM (
    'light',
    'medium',
    'heavy'
  );
EXCEPTION WHEN duplicate_object THEN null;
END $$;

DO $$ BEGIN
  CREATE TYPE despacho.accion_despacho AS ENUM (
    'created',
    'assigned',
    'reassigned',
    'rejected',
    'escalated',
    'cancelled',
    'acknowledged',
    'completed'
  );
EXCEPTION WHEN duplicate_object THEN null;
END $$;

DO $$ BEGIN
  CREATE TYPE despacho.canal_notificacion AS ENUM (
    'email',
    'sms',
    'whatsapp',
    'push',
    'call'
  );
EXCEPTION WHEN duplicate_object THEN null;
END $$;

-- =============================================================================
-- TABLA: tableros_despacho
-- =============================================================================

CREATE TABLE IF NOT EXISTS despacho.tableros_despacho (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES public.tenants(id),

  nombre VARCHAR(100) NOT NULL,
  descripcion TEXT,

  -- Map defaults
  default_zoom INTEGER DEFAULT 12,
  centro_lat DECIMAL(10, 7) DEFAULT 19.4326,
  centro_lng DECIMAL(10, 7) DEFAULT -99.1332,

  -- Behavior
  intervalo_refresco_segundos INTEGER DEFAULT 30,
  mostrar_unidades_offline BOOLEAN DEFAULT TRUE,
  auto_asignar_habilitado BOOLEAN DEFAULT FALSE,
  max_sugerencias INTEGER DEFAULT 5,

  -- Filters
  filtros_default JSONB DEFAULT '{}',

  activo BOOLEAN DEFAULT TRUE,

  -- Audit
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  created_by UUID
);

CREATE INDEX IF NOT EXISTS idx_tableros_despacho_tenant ON despacho.tableros_despacho(tenant_id);

-- =============================================================================
-- TABLA: estado_unidades
-- =============================================================================

CREATE TABLE IF NOT EXISTS despacho.estado_unidades (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES public.tenants(id),

  -- Unit reference (FK a fleet.unidades)
  unidad_id UUID NOT NULL,
  codigo_unidad VARCHAR(50),
  nombre_unidad VARCHAR(100),

  -- Status
  estado despacho.estado_unidad DEFAULT 'offline',

  -- Current assignment (FK a transport.viajes)
  viaje_actual_id UUID,
  operador_ids UUID[] DEFAULT '{}',

  -- Location (cached from GPS)
  ultima_posicion_id UUID,
  ultima_posicion_lat DECIMAL(10, 7),
  ultima_posicion_lng DECIMAL(10, 7),
  ultima_actualizacion_ubicacion TIMESTAMPTZ,

  -- Timing
  ultimo_cambio_estado TIMESTAMPTZ DEFAULT NOW(),
  disponible_estimado_en TIMESTAMPTZ,

  -- Capacity and capabilities
  capacidad_unidad despacho.capacidad_unidad DEFAULT 'light',
  puede_remolcar BOOLEAN DEFAULT FALSE,
  peso_max_remolque_kg INTEGER,

  -- Transport-specific
  es_refrigerada BOOLEAN DEFAULT FALSE,
  capacidad_peso_kg DECIMAL(10, 2),

  notas TEXT,
  metadata JSONB DEFAULT '{}',

  updated_at TIMESTAMPTZ DEFAULT NOW(),

  CONSTRAINT uq_estado_unidad UNIQUE (tenant_id, unidad_id)
);

CREATE INDEX IF NOT EXISTS idx_estado_unidades_tenant ON despacho.estado_unidades(tenant_id);
CREATE INDEX IF NOT EXISTS idx_estado_unidades_estado ON despacho.estado_unidades(tenant_id, estado);
CREATE INDEX IF NOT EXISTS idx_estado_unidades_viaje ON despacho.estado_unidades(viaje_actual_id) WHERE viaje_actual_id IS NOT NULL;

-- =============================================================================
-- TABLA: reglas_despacho
-- =============================================================================

CREATE TABLE IF NOT EXISTS despacho.reglas_despacho (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES public.tenants(id),

  nombre VARCHAR(100) NOT NULL,
  descripcion TEXT,

  prioridad INTEGER DEFAULT 0,

  -- Applicability
  tipo_viaje VARCHAR(50),
  categoria_viaje VARCHAR(50),

  -- Conditions (JSONB with transport-specific rules)
  condiciones JSONB NOT NULL DEFAULT '{}',

  -- Action
  auto_asignar BOOLEAN DEFAULT FALSE,
  peso_asignacion INTEGER DEFAULT 100,

  activo BOOLEAN DEFAULT TRUE,

  -- Audit
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  created_by UUID
);

CREATE INDEX IF NOT EXISTS idx_reglas_despacho_tenant ON despacho.reglas_despacho(tenant_id);
CREATE INDEX IF NOT EXISTS idx_reglas_despacho_prioridad ON despacho.reglas_despacho(tenant_id, prioridad DESC);

-- =============================================================================
-- TABLA: reglas_escalamiento
-- =============================================================================

CREATE TABLE IF NOT EXISTS despacho.reglas_escalamiento (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES public.tenants(id),

  nombre VARCHAR(100) NOT NULL,
  descripcion TEXT,

  -- Trigger conditions
  disparar_despues_minutos INTEGER NOT NULL,
  disparar_estado VARCHAR(50),
  disparar_prioridad VARCHAR(20),

  -- Escalation target
  escalar_a_rol VARCHAR(50) NOT NULL,
  escalar_a_usuarios UUID[],

  -- Notification (default whatsapp for transport)
  canal_notificacion despacho.canal_notificacion DEFAULT 'whatsapp',
  plantilla_notificacion TEXT,
  datos_notificacion JSONB DEFAULT '{}',

  -- Repeat
  intervalo_repeticion_minutos INTEGER,
  max_escalamientos INTEGER DEFAULT 3,

  activo BOOLEAN DEFAULT TRUE,

  -- Audit
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  created_by UUID
);

CREATE INDEX IF NOT EXISTS idx_reglas_escalamiento_tenant ON despacho.reglas_escalamiento(tenant_id);
CREATE INDEX IF NOT EXISTS idx_reglas_escalamiento_trigger ON despacho.reglas_escalamiento(tenant_id, disparar_despues_minutos);

-- =============================================================================
-- TABLA: log_despacho
-- =============================================================================

CREATE TABLE IF NOT EXISTS despacho.log_despacho (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES public.tenants(id),

  -- Viaje reference (FK a transport.viajes)
  viaje_id UUID NOT NULL,

  -- Action
  accion despacho.accion_despacho NOT NULL,

  -- Unit/Operador changes
  desde_unidad_id UUID,
  hacia_unidad_id UUID,
  desde_operador_id UUID,
  hacia_operador_id UUID,

  -- Context
  razon TEXT,
  automatizado BOOLEAN DEFAULT FALSE,
  regla_id UUID,
  escalamiento_id UUID,

  -- Response times
  tiempo_respuesta_segundos INTEGER,

  -- Actor
  ejecutado_por UUID,
  ejecutado_en TIMESTAMPTZ DEFAULT NOW(),

  -- Extra data
  metadata JSONB DEFAULT '{}'
);

CREATE INDEX IF NOT EXISTS idx_log_despacho_tenant ON despacho.log_despacho(tenant_id);
CREATE INDEX IF NOT EXISTS idx_log_despacho_viaje ON despacho.log_despacho(tenant_id, viaje_id);
CREATE INDEX IF NOT EXISTS idx_log_despacho_fecha ON despacho.log_despacho(tenant_id, ejecutado_en DESC);
CREATE INDEX IF NOT EXISTS idx_log_despacho_accion ON despacho.log_despacho(tenant_id, accion);

-- =============================================================================
-- TABLAS ADICIONALES EN FLEET (certificaciones y turnos)
-- =============================================================================

-- TABLA: certificaciones_operador
CREATE TABLE IF NOT EXISTS fleet.certificaciones_operador (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES public.tenants(id),

  -- Operador reference (FK a fleet.operadores)
  operador_id UUID NOT NULL,

  -- Certification definition
  codigo_certificacion VARCHAR(50) NOT NULL,
  nombre_certificacion VARCHAR(100) NOT NULL,
  descripcion TEXT,

  -- Level
  nivel VARCHAR(20) DEFAULT 'basico',

  -- Certification details
  numero_certificado VARCHAR(100),
  fecha_certificacion DATE,
  vigencia_hasta DATE,
  documento_url TEXT,

  -- Status
  activa BOOLEAN DEFAULT TRUE,
  verificado_por UUID,
  verificado_en TIMESTAMPTZ,

  -- Audit
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),

  CONSTRAINT uq_operador_certificacion UNIQUE(tenant_id, operador_id, codigo_certificacion)
);

CREATE INDEX IF NOT EXISTS idx_certificaciones_operador ON fleet.certificaciones_operador(operador_id);
CREATE INDEX IF NOT EXISTS idx_certificaciones_tenant ON fleet.certificaciones_operador(tenant_id);
CREATE INDEX IF NOT EXISTS idx_certificaciones_vencimiento ON fleet.certificaciones_operador(vigencia_hasta) WHERE activa = TRUE;

-- TABLA: turnos_operador
CREATE TABLE IF NOT EXISTS fleet.turnos_operador (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES public.tenants(id),

  -- Operador reference (FK a fleet.operadores)
  operador_id UUID NOT NULL,

  -- Schedule
  fecha_turno DATE NOT NULL,
  tipo_turno VARCHAR(20) NOT NULL,
  hora_inicio TIME NOT NULL,
  hora_fin TIME NOT NULL,

  -- On-call specifics
  en_guardia BOOLEAN DEFAULT FALSE,
  prioridad_guardia INTEGER DEFAULT 0,

  -- Assignment
  unidad_asignada_id UUID,

  -- Status
  hora_inicio_real TIMESTAMPTZ,
  hora_fin_real TIMESTAMPTZ,
  ausente BOOLEAN DEFAULT FALSE,
  motivo_ausencia TEXT,

  notas TEXT,

  -- Audit
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  created_by UUID
);

CREATE INDEX IF NOT EXISTS idx_turnos_operador ON fleet.turnos_operador(operador_id, fecha_turno);
CREATE INDEX IF NOT EXISTS idx_turnos_tenant ON fleet.turnos_operador(tenant_id);
CREATE INDEX IF NOT EXISTS idx_turnos_fecha ON fleet.turnos_operador(tenant_id, fecha_turno);

-- =============================================================================
-- RLS POLICIES
-- =============================================================================

ALTER TABLE despacho.tableros_despacho ENABLE ROW LEVEL SECURITY;
ALTER TABLE despacho.estado_unidades ENABLE ROW LEVEL SECURITY;
ALTER TABLE despacho.reglas_despacho ENABLE ROW LEVEL SECURITY;
ALTER TABLE despacho.reglas_escalamiento ENABLE ROW LEVEL SECURITY;
ALTER TABLE despacho.log_despacho ENABLE ROW LEVEL SECURITY;
ALTER TABLE fleet.certificaciones_operador ENABLE ROW LEVEL SECURITY;
ALTER TABLE fleet.turnos_operador ENABLE ROW LEVEL SECURITY;

DO $$ BEGIN
  CREATE POLICY tenant_isolation_tableros ON despacho.tableros_despacho
    USING (tenant_id = current_setting('app.tenant_id')::uuid);
EXCEPTION WHEN duplicate_object THEN null;
END $$;

DO $$ BEGIN
  CREATE POLICY tenant_isolation_estado ON despacho.estado_unidades
    USING (tenant_id = current_setting('app.tenant_id')::uuid);
EXCEPTION WHEN duplicate_object THEN null;
END $$;

DO $$ BEGIN
  CREATE POLICY tenant_isolation_reglas ON despacho.reglas_despacho
    USING (tenant_id = current_setting('app.tenant_id')::uuid);
EXCEPTION WHEN duplicate_object THEN null;
END $$;

DO $$ BEGIN
  CREATE POLICY tenant_isolation_escalamiento ON despacho.reglas_escalamiento
    USING (tenant_id = current_setting('app.tenant_id')::uuid);
EXCEPTION WHEN duplicate_object THEN null;
END $$;

DO $$ BEGIN
  CREATE POLICY tenant_isolation_log ON despacho.log_despacho
    USING (tenant_id = current_setting('app.tenant_id')::uuid);
EXCEPTION WHEN duplicate_object THEN null;
END $$;

DO $$ BEGIN
  CREATE POLICY tenant_isolation_certificaciones ON fleet.certificaciones_operador
    USING (tenant_id = current_setting('app.tenant_id')::uuid);
EXCEPTION WHEN duplicate_object THEN null;
END $$;

DO $$ BEGIN
  CREATE POLICY tenant_isolation_turnos ON fleet.turnos_operador
    USING (tenant_id = current_setting('app.tenant_id')::uuid);
EXCEPTION WHEN duplicate_object THEN null;
END $$;

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

COMMENT ON SCHEMA despacho IS 'Modulo de despacho para asignacion de viajes a unidades/operadores';

COMMENT ON TABLE despacho.tableros_despacho IS 'Configuracion de tableros de despacho con mapa';
COMMENT ON TABLE despacho.estado_unidades IS 'Estado en tiempo real de unidades para despacho';
COMMENT ON TABLE despacho.reglas_despacho IS 'Reglas para asignacion automatica de viajes';
COMMENT ON TABLE despacho.reglas_escalamiento IS 'Reglas para escalar viajes sin respuesta';
COMMENT ON TABLE despacho.log_despacho IS 'Auditoria de acciones de despacho';

COMMENT ON TABLE fleet.certificaciones_operador IS 'Certificaciones y licencias de operadores';
COMMENT ON TABLE fleet.turnos_operador IS 'Programacion de turnos de operadores';

COMMENT ON COLUMN despacho.estado_unidades.viaje_actual_id IS 'FK a transport.viajes - viaje actualmente asignado';
COMMENT ON COLUMN despacho.estado_unidades.operador_ids IS 'IDs de operadores asignados a la unidad';
COMMENT ON COLUMN despacho.log_despacho.viaje_id IS 'FK a transport.viajes - viaje al que aplica la accion';

-- =============================================================================
-- FIN DDL DESPACHO
-- =============================================================================
