-- ============================================================
-- 16-dispatch-schema.sql
-- Mecanicas Diesel - ERP Suite
--
-- Dispatch Center Schema (MMD-011)
-- Centro de despacho para asignacion de incidentes
-- ============================================================

-- Create dispatch schema
CREATE SCHEMA IF NOT EXISTS dispatch;

-- ============================================================
-- ENUMS
-- ============================================================

-- Unit operational status
CREATE TYPE dispatch.unit_status_enum AS ENUM (
    'available',
    'assigned',
    'en_route',
    'on_site',
    'returning',
    'offline',
    'maintenance'
);

-- Technician shift types
CREATE TYPE dispatch.shift_type AS ENUM (
    'morning',
    'afternoon',
    'night',
    'full_day',
    'on_call'
);

-- Skill levels
CREATE TYPE dispatch.skill_level AS ENUM (
    'basic',
    'intermediate',
    'advanced',
    'expert'
);

-- Escalation notification channels
CREATE TYPE dispatch.notification_channel AS ENUM (
    'email',
    'sms',
    'whatsapp',
    'push',
    'call'
);

-- Dispatch log actions
CREATE TYPE dispatch.dispatch_action AS ENUM (
    'created',
    'assigned',
    'reassigned',
    'rejected',
    'escalated',
    'cancelled',
    'acknowledged',
    'completed'
);

-- ============================================================
-- TABLES
-- ============================================================

-- Dispatch Board Configuration
CREATE TABLE dispatch.dispatch_boards (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,
    name VARCHAR(100) NOT NULL,
    description TEXT,

    -- Map defaults
    default_zoom INTEGER DEFAULT 12,
    center_lat DECIMAL(10, 7) DEFAULT 19.4326,
    center_lng DECIMAL(10, 7) DEFAULT -99.1332,

    -- Behavior
    refresh_interval_seconds INTEGER DEFAULT 30,
    show_offline_units BOOLEAN DEFAULT true,
    auto_assign_enabled BOOLEAN DEFAULT false,
    max_suggestions INTEGER DEFAULT 5,

    -- Filters
    default_filters JSONB DEFAULT '{}',

    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    created_by UUID
);

CREATE INDEX idx_dispatch_boards_tenant ON dispatch.dispatch_boards(tenant_id);

-- Unit Status (real-time unit state)
CREATE TABLE dispatch.unit_statuses (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Unit reference
    unit_id UUID NOT NULL,
    unit_code VARCHAR(50),
    unit_name VARCHAR(100),

    -- Status
    status dispatch.unit_status_enum DEFAULT 'offline',

    -- Current assignment
    current_incident_id UUID,
    current_technician_ids UUID[] DEFAULT '{}',

    -- Location (cached from GPS)
    last_position_id UUID,
    last_known_lat DECIMAL(10, 7),
    last_known_lng DECIMAL(10, 7),
    last_location_update TIMESTAMPTZ,

    -- Timing
    last_status_change TIMESTAMPTZ DEFAULT NOW(),
    estimated_available_at TIMESTAMPTZ,

    -- Capacity and capabilities
    unit_capacity VARCHAR(20) DEFAULT 'light', -- light, medium, heavy
    can_tow BOOLEAN DEFAULT false,
    max_tow_weight_kg INTEGER,

    notes TEXT,
    metadata JSONB DEFAULT '{}',

    updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE UNIQUE INDEX idx_unit_statuses_tenant_unit ON dispatch.unit_statuses(tenant_id, unit_id);
CREATE INDEX idx_unit_statuses_status ON dispatch.unit_statuses(tenant_id, status);
CREATE INDEX idx_unit_statuses_current_incident ON dispatch.unit_statuses(current_incident_id) WHERE current_incident_id IS NOT NULL;

-- Technician Skills
CREATE TABLE dispatch.technician_skills (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Technician reference
    technician_id UUID NOT NULL,

    -- Skill definition
    skill_code VARCHAR(50) NOT NULL,
    skill_name VARCHAR(100) NOT NULL,
    skill_description TEXT,
    level dispatch.skill_level DEFAULT 'basic',

    -- Certification
    certification_number VARCHAR(100),
    certified_at DATE,
    expires_at DATE,
    certification_document_url VARCHAR(500),

    -- Status
    is_active BOOLEAN DEFAULT true,
    verified_by UUID,
    verified_at TIMESTAMPTZ,

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

    CONSTRAINT uq_technician_skill UNIQUE(tenant_id, technician_id, skill_code)
);

CREATE INDEX idx_technician_skills_tenant ON dispatch.technician_skills(tenant_id);
CREATE INDEX idx_technician_skills_technician ON dispatch.technician_skills(tenant_id, technician_id);
CREATE INDEX idx_technician_skills_code ON dispatch.technician_skills(tenant_id, skill_code);
CREATE INDEX idx_technician_skills_expiry ON dispatch.technician_skills(expires_at) WHERE expires_at IS NOT NULL;

-- Technician Shifts
CREATE TABLE dispatch.technician_shifts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Technician reference
    technician_id UUID NOT NULL,

    -- Schedule
    shift_date DATE NOT NULL,
    shift_type dispatch.shift_type NOT NULL,
    start_time TIME NOT NULL,
    end_time TIME NOT NULL,

    -- On-call specifics
    is_on_call BOOLEAN DEFAULT false,
    on_call_priority INTEGER DEFAULT 0,

    -- Assignment
    assigned_unit_id UUID,

    -- Status
    actual_start_time TIMESTAMPTZ,
    actual_end_time TIMESTAMPTZ,
    is_absent BOOLEAN DEFAULT false,
    absence_reason TEXT,

    notes TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    created_by UUID
);

CREATE INDEX idx_technician_shifts_tenant ON dispatch.technician_shifts(tenant_id);
CREATE INDEX idx_technician_shifts_technician_date ON dispatch.technician_shifts(tenant_id, technician_id, shift_date);
CREATE INDEX idx_technician_shifts_date ON dispatch.technician_shifts(tenant_id, shift_date);
CREATE INDEX idx_technician_shifts_unit ON dispatch.technician_shifts(assigned_unit_id) WHERE assigned_unit_id IS NOT NULL;

-- Dispatch Rules (assignment criteria)
CREATE TABLE dispatch.dispatch_rules (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    name VARCHAR(100) NOT NULL,
    description TEXT,
    priority INTEGER DEFAULT 0,

    -- Applicability
    service_type_code VARCHAR(50),
    incident_category VARCHAR(50),

    -- Conditions (JSON structure for flexibility)
    conditions JSONB NOT NULL DEFAULT '{}',
    /*
    conditions structure:
    {
        "requires_skills": ["DIESEL_ENGINE", "ELECTRICAL"],
        "requires_asset_categories": ["DIAGNOSTIC_TOOL"],
        "min_unit_capacity": "heavy",
        "max_distance_km": 50,
        "zone_codes": ["NORTE", "CENTRO"],
        "min_skill_level": "intermediate",
        "exclude_on_call": false
    }
    */

    -- Action
    auto_assign BOOLEAN DEFAULT false,
    assignment_weight INTEGER DEFAULT 100,

    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    created_by UUID
);

CREATE INDEX idx_dispatch_rules_tenant ON dispatch.dispatch_rules(tenant_id);
CREATE INDEX idx_dispatch_rules_service_type ON dispatch.dispatch_rules(tenant_id, service_type_code) WHERE service_type_code IS NOT NULL;
CREATE INDEX idx_dispatch_rules_priority ON dispatch.dispatch_rules(tenant_id, priority DESC);

-- Escalation Rules
CREATE TABLE dispatch.escalation_rules (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    name VARCHAR(100) NOT NULL,
    description TEXT,

    -- Trigger conditions
    trigger_after_minutes INTEGER NOT NULL,
    trigger_status VARCHAR(50), -- incident status that triggers
    trigger_priority VARCHAR(20), -- only for specific priorities

    -- Escalation target
    escalate_to_role VARCHAR(50) NOT NULL,
    escalate_to_user_ids UUID[],

    -- Notification
    notification_channel dispatch.notification_channel NOT NULL,
    notification_template TEXT,
    notification_data JSONB DEFAULT '{}',

    -- Repeat
    repeat_interval_minutes INTEGER,
    max_escalations INTEGER DEFAULT 3,

    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    created_by UUID
);

CREATE INDEX idx_escalation_rules_tenant ON dispatch.escalation_rules(tenant_id);
CREATE INDEX idx_escalation_rules_trigger ON dispatch.escalation_rules(tenant_id, trigger_after_minutes);

-- Dispatch Log (audit trail)
CREATE TABLE dispatch.dispatch_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Incident reference
    incident_id UUID NOT NULL,

    -- Action
    action dispatch.dispatch_action NOT NULL,

    -- Unit/Technician changes
    from_unit_id UUID,
    to_unit_id UUID,
    from_technician_id UUID,
    to_technician_id UUID,

    -- Context
    reason TEXT,
    automated BOOLEAN DEFAULT false,
    rule_id UUID, -- dispatch rule that caused action
    escalation_id UUID, -- escalation rule that caused action

    -- Response times
    response_time_seconds INTEGER,

    -- Actor
    performed_by UUID,
    performed_at TIMESTAMPTZ DEFAULT NOW(),

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

CREATE INDEX idx_dispatch_logs_tenant ON dispatch.dispatch_logs(tenant_id);
CREATE INDEX idx_dispatch_logs_incident ON dispatch.dispatch_logs(tenant_id, incident_id);
CREATE INDEX idx_dispatch_logs_time ON dispatch.dispatch_logs(tenant_id, performed_at DESC);
CREATE INDEX idx_dispatch_logs_action ON dispatch.dispatch_logs(tenant_id, action);

-- ============================================================
-- TRIGGERS
-- ============================================================

-- Auto-update updated_at
CREATE OR REPLACE FUNCTION dispatch.update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_dispatch_boards_updated_at
    BEFORE UPDATE ON dispatch.dispatch_boards
    FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();

CREATE TRIGGER trg_unit_statuses_updated_at
    BEFORE UPDATE ON dispatch.unit_statuses
    FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();

CREATE TRIGGER trg_technician_skills_updated_at
    BEFORE UPDATE ON dispatch.technician_skills
    FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();

CREATE TRIGGER trg_technician_shifts_updated_at
    BEFORE UPDATE ON dispatch.technician_shifts
    FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();

CREATE TRIGGER trg_dispatch_rules_updated_at
    BEFORE UPDATE ON dispatch.dispatch_rules
    FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();

CREATE TRIGGER trg_escalation_rules_updated_at
    BEFORE UPDATE ON dispatch.escalation_rules
    FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();

-- Log unit status changes
CREATE OR REPLACE FUNCTION dispatch.log_unit_status_change()
RETURNS TRIGGER AS $$
BEGIN
    IF OLD.status IS DISTINCT FROM NEW.status THEN
        NEW.last_status_change = NOW();
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_unit_status_change
    BEFORE UPDATE ON dispatch.unit_statuses
    FOR EACH ROW EXECUTE FUNCTION dispatch.log_unit_status_change();

-- ============================================================
-- VIEWS
-- ============================================================

-- Available units with current position
CREATE OR REPLACE VIEW dispatch.vw_available_units AS
SELECT
    us.id,
    us.tenant_id,
    us.unit_id,
    us.unit_code,
    us.unit_name,
    us.status,
    us.last_known_lat,
    us.last_known_lng,
    us.last_location_update,
    us.unit_capacity,
    us.can_tow,
    us.max_tow_weight_kg,
    us.estimated_available_at,
    EXTRACT(EPOCH FROM (NOW() - us.last_location_update)) / 60 AS minutes_since_update
FROM dispatch.unit_statuses us
WHERE us.status = 'available';

-- Technicians on shift today
CREATE OR REPLACE VIEW dispatch.vw_technicians_on_shift AS
SELECT
    ts.id,
    ts.tenant_id,
    ts.technician_id,
    ts.shift_date,
    ts.shift_type,
    ts.start_time,
    ts.end_time,
    ts.is_on_call,
    ts.assigned_unit_id,
    ts.is_absent,
    CASE
        WHEN ts.is_absent THEN 'absent'
        WHEN ts.actual_start_time IS NOT NULL AND ts.actual_end_time IS NULL THEN 'active'
        WHEN ts.actual_start_time IS NULL THEN 'pending'
        ELSE 'completed'
    END AS shift_status
FROM dispatch.technician_shifts ts
WHERE ts.shift_date = CURRENT_DATE
  AND ts.is_absent = false;

-- Skills about to expire
CREATE OR REPLACE VIEW dispatch.vw_expiring_skills AS
SELECT
    tsk.id,
    tsk.tenant_id,
    tsk.technician_id,
    tsk.skill_code,
    tsk.skill_name,
    tsk.level,
    tsk.expires_at,
    tsk.expires_at - CURRENT_DATE AS days_until_expiry
FROM dispatch.technician_skills tsk
WHERE tsk.is_active = true
  AND tsk.expires_at IS NOT NULL
  AND tsk.expires_at <= CURRENT_DATE + INTERVAL '30 days';

-- Dispatch activity summary
CREATE OR REPLACE VIEW dispatch.vw_dispatch_activity AS
SELECT
    dl.tenant_id,
    DATE(dl.performed_at) AS activity_date,
    dl.action,
    COUNT(*) AS count,
    AVG(dl.response_time_seconds) AS avg_response_time_seconds,
    COUNT(CASE WHEN dl.automated THEN 1 END) AS automated_count
FROM dispatch.dispatch_logs dl
WHERE dl.performed_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY dl.tenant_id, DATE(dl.performed_at), dl.action;

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

ALTER TABLE dispatch.dispatch_boards ENABLE ROW LEVEL SECURITY;
ALTER TABLE dispatch.unit_statuses ENABLE ROW LEVEL SECURITY;
ALTER TABLE dispatch.technician_skills ENABLE ROW LEVEL SECURITY;
ALTER TABLE dispatch.technician_shifts ENABLE ROW LEVEL SECURITY;
ALTER TABLE dispatch.dispatch_rules ENABLE ROW LEVEL SECURITY;
ALTER TABLE dispatch.escalation_rules ENABLE ROW LEVEL SECURITY;
ALTER TABLE dispatch.dispatch_logs ENABLE ROW LEVEL SECURITY;

-- RLS Policies for each table
CREATE POLICY tenant_isolation_dispatch_boards ON dispatch.dispatch_boards
    FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);

CREATE POLICY tenant_isolation_unit_statuses ON dispatch.unit_statuses
    FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);

CREATE POLICY tenant_isolation_technician_skills ON dispatch.technician_skills
    FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);

CREATE POLICY tenant_isolation_technician_shifts ON dispatch.technician_shifts
    FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);

CREATE POLICY tenant_isolation_dispatch_rules ON dispatch.dispatch_rules
    FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);

CREATE POLICY tenant_isolation_escalation_rules ON dispatch.escalation_rules
    FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);

CREATE POLICY tenant_isolation_dispatch_logs ON dispatch.dispatch_logs
    FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);

-- ============================================================
-- COMMENTS
-- ============================================================

COMMENT ON SCHEMA dispatch IS 'Dispatch Center schema for incident assignment and unit management (MMD-011)';
COMMENT ON TABLE dispatch.dispatch_boards IS 'Dispatch board configurations for map and assignment views';
COMMENT ON TABLE dispatch.unit_statuses IS 'Real-time status of fleet units for dispatch';
COMMENT ON TABLE dispatch.technician_skills IS 'Skills and certifications of technicians';
COMMENT ON TABLE dispatch.technician_shifts IS 'Shift schedules for technicians';
COMMENT ON TABLE dispatch.dispatch_rules IS 'Rules for automatic assignment suggestions';
COMMENT ON TABLE dispatch.escalation_rules IS 'Rules for escalating unresponded incidents';
COMMENT ON TABLE dispatch.dispatch_logs IS 'Audit log of all dispatch actions';
