-- ===========================================
-- MECANICAS DIESEL - Schema de Notificaciones
-- ===========================================
-- Sistema de tracking, mensajes, followers y actividades
-- Permite historial de cambios y notificaciones automaticas

-- ============================================
-- SCHEMA: notifications
-- ============================================
CREATE SCHEMA IF NOT EXISTS notifications;
COMMENT ON SCHEMA notifications IS 'Sistema de tracking, mensajes, followers y actividades (patron mail.thread)';

-- Grants
GRANT USAGE ON SCHEMA notifications TO mecanicas_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA notifications TO mecanicas_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA notifications GRANT ALL ON TABLES TO mecanicas_user;

-- ============================================
-- GAP-01: Sistema de Tracking de Cambios
-- ============================================

-- Subtipos de mensaje (clasificacion)
CREATE TABLE notifications.message_subtypes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    code VARCHAR(50) NOT NULL UNIQUE,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    res_model VARCHAR(100),           -- NULL = aplica a todos los modelos
    is_internal BOOLEAN NOT NULL DEFAULT false,
    is_default BOOLEAN NOT NULL DEFAULT false,
    sequence INTEGER NOT NULL DEFAULT 10,
    is_active BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE notifications.message_subtypes IS 'Clasificacion de tipos de mensaje (creacion, edicion, nota, etc.)';

-- Seed de subtipos predeterminados
INSERT INTO notifications.message_subtypes (code, name, description, is_internal, is_default, sequence) VALUES
('mt_note', 'Nota', 'Nota interna', true, true, 1),
('mt_comment', 'Comentario', 'Comentario publico', false, true, 2),
('mt_tracking', 'Cambio de valor', 'Cambio en campo trackeado', true, false, 10),
('mt_creation', 'Creacion', 'Documento creado', false, false, 5),
('mt_status_change', 'Cambio de estado', 'Estado del documento modificado', false, false, 6),
('mt_assignment', 'Asignacion', 'Documento asignado a usuario', false, false, 7);

-- Mensajes (chatter/historial)
CREATE TABLE notifications.messages (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Referencia al documento
    res_model VARCHAR(100) NOT NULL,          -- ej: 'service_management.service_orders'
    res_id UUID NOT NULL,                      -- ID del documento

    -- Tipo y subtipo
    message_type VARCHAR(20) NOT NULL DEFAULT 'notification',
    subtype_id UUID REFERENCES notifications.message_subtypes(id),

    -- Autor
    author_id UUID,                            -- Usuario que escribio el mensaje
    author_name VARCHAR(256),                  -- Nombre del autor (desnormalizado)

    -- Contenido
    subject VARCHAR(500),
    body TEXT,

    -- Tracking de cambios (JSON array)
    -- Formato: [{"field": "status", "old_value": "draft", "new_value": "confirmed", "field_label": "Estado"}]
    tracking_values JSONB DEFAULT '[]'::jsonb,

    -- Metadatos
    is_internal BOOLEAN NOT NULL DEFAULT false,
    parent_id UUID REFERENCES notifications.messages(id),

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    CONSTRAINT chk_message_type CHECK (message_type IN ('comment', 'notification', 'note', 'email', 'system'))
);

COMMENT ON TABLE notifications.messages IS 'Historial de mensajes y cambios en documentos (chatter)';
COMMENT ON COLUMN notifications.messages.res_model IS 'Nombre completo del modelo (schema.table)';
COMMENT ON COLUMN notifications.messages.res_id IS 'ID del registro referenciado';
COMMENT ON COLUMN notifications.messages.tracking_values IS 'Array JSON con cambios de campos trackeados';

-- Indices para messages
CREATE INDEX idx_messages_resource ON notifications.messages(res_model, res_id);
CREATE INDEX idx_messages_tenant ON notifications.messages(tenant_id);
CREATE INDEX idx_messages_created ON notifications.messages(created_at DESC);
CREATE INDEX idx_messages_author ON notifications.messages(author_id);
CREATE INDEX idx_messages_parent ON notifications.messages(parent_id) WHERE parent_id IS NOT NULL;

-- RLS para messages
SELECT create_tenant_rls_policies('notifications', 'messages');

-- ============================================
-- GAP-02: Sistema de Followers/Suscriptores
-- ============================================

-- Seguidores de documentos
CREATE TABLE notifications.followers (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Referencia al documento
    res_model VARCHAR(100) NOT NULL,
    res_id UUID NOT NULL,

    -- Seguidor (puede ser usuario o partner/cliente)
    partner_id UUID NOT NULL,                  -- ID del contacto/usuario
    partner_type VARCHAR(20) NOT NULL DEFAULT 'user',

    -- Metadatos
    reason VARCHAR(100),                       -- Por que sigue (manual, asignacion, creador, etc.)
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    CONSTRAINT uq_follower UNIQUE(tenant_id, res_model, res_id, partner_id),
    CONSTRAINT chk_partner_type CHECK (partner_type IN ('user', 'customer', 'supplier'))
);

COMMENT ON TABLE notifications.followers IS 'Suscriptores a documentos para notificaciones automaticas';

-- Subtipos a los que esta suscrito cada follower
CREATE TABLE notifications.follower_subtypes (
    follower_id UUID NOT NULL REFERENCES notifications.followers(id) ON DELETE CASCADE,
    subtype_id UUID NOT NULL REFERENCES notifications.message_subtypes(id) ON DELETE CASCADE,
    PRIMARY KEY (follower_id, subtype_id)
);

COMMENT ON TABLE notifications.follower_subtypes IS 'Tipos de mensaje a los que esta suscrito cada follower';

-- Indices para followers
CREATE INDEX idx_followers_resource ON notifications.followers(res_model, res_id);
CREATE INDEX idx_followers_partner ON notifications.followers(partner_id);
CREATE INDEX idx_followers_tenant ON notifications.followers(tenant_id);

-- RLS para followers
SELECT create_tenant_rls_policies('notifications', 'followers');

-- ============================================
-- GAP-03: Actividades Programadas
-- ============================================

-- Tipos de actividad
CREATE TABLE notifications.activity_types (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    code VARCHAR(50) NOT NULL UNIQUE,
    name VARCHAR(100) NOT NULL,
    description TEXT,

    -- Configuracion
    icon VARCHAR(50) DEFAULT 'fa-tasks',
    color VARCHAR(20) DEFAULT 'primary',
    default_days INTEGER DEFAULT 0,            -- Dias por defecto para deadline

    -- Restriccion por modelo (NULL = todos)
    res_model VARCHAR(100),

    -- Metadatos
    sequence INTEGER NOT NULL DEFAULT 10,
    is_active BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE notifications.activity_types IS 'Tipos de actividad disponibles (llamar, reunion, tarea, etc.)';

-- Seed de tipos predeterminados para taller mecanico
INSERT INTO notifications.activity_types (code, name, description, icon, color, default_days, sequence) VALUES
('call', 'Llamar cliente', 'Llamada telefonica al cliente', 'fa-phone', 'info', 0, 1),
('meeting', 'Cita de entrega', 'Cita para entregar vehiculo', 'fa-calendar-check', 'success', 0, 2),
('todo', 'Tarea pendiente', 'Tarea generica por completar', 'fa-tasks', 'warning', 1, 3),
('reminder', 'Recordatorio mantenimiento', 'Recordar cliente sobre proximo mantenimiento', 'fa-bell', 'secondary', 30, 4),
('followup', 'Seguimiento cotizacion', 'Dar seguimiento a cotizacion enviada', 'fa-envelope', 'primary', 3, 5),
('approval', 'Pendiente aprobacion', 'Esperar aprobacion de cliente o supervisor', 'fa-check-circle', 'danger', 1, 6),
('parts_arrival', 'Llegada de refacciones', 'Refacciones pendientes de llegar', 'fa-truck', 'info', 2, 7),
('quality_check', 'Revision de calidad', 'Inspeccion de trabajo terminado', 'fa-search', 'warning', 0, 8);

-- Actividades programadas
CREATE TABLE notifications.activities (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Referencia al documento
    res_model VARCHAR(100) NOT NULL,
    res_id UUID NOT NULL,

    -- Tipo y asignacion
    activity_type_id UUID NOT NULL REFERENCES notifications.activity_types(id),
    user_id UUID NOT NULL,                     -- Usuario asignado

    -- Programacion
    date_deadline DATE NOT NULL,

    -- Contenido
    summary VARCHAR(500),
    note TEXT,

    -- Estado
    state VARCHAR(20) NOT NULL DEFAULT 'planned',
    date_done TIMESTAMPTZ,
    feedback TEXT,                             -- Comentario al completar

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,

    CONSTRAINT chk_activity_state CHECK (state IN ('planned', 'today', 'overdue', 'done', 'canceled'))
);

COMMENT ON TABLE notifications.activities IS 'Actividades y recordatorios programados asociados a documentos';
COMMENT ON COLUMN notifications.activities.state IS 'Estado: planned (futuro), today (hoy), overdue (vencida), done (completada), canceled';

-- Indices para activities
CREATE INDEX idx_activities_resource ON notifications.activities(res_model, res_id);
CREATE INDEX idx_activities_user ON notifications.activities(user_id);
CREATE INDEX idx_activities_deadline ON notifications.activities(date_deadline);
CREATE INDEX idx_activities_tenant ON notifications.activities(tenant_id);
CREATE INDEX idx_activities_pending ON notifications.activities(user_id, date_deadline)
    WHERE state NOT IN ('done', 'canceled');

-- RLS para activities
SELECT create_tenant_rls_policies('notifications', 'activities');

-- ============================================
-- FUNCIONES AUXILIARES
-- ============================================

-- Funcion para actualizar estado de actividades (planned -> today -> overdue)
CREATE OR REPLACE FUNCTION notifications.update_activity_states()
RETURNS INTEGER AS $$
DECLARE
    v_updated INTEGER;
BEGIN
    -- Actualizar a 'today' las que vencen hoy
    UPDATE notifications.activities
    SET state = 'today'
    WHERE state = 'planned'
      AND date_deadline = CURRENT_DATE;

    GET DIAGNOSTICS v_updated = ROW_COUNT;

    -- Actualizar a 'overdue' las vencidas
    UPDATE notifications.activities
    SET state = 'overdue'
    WHERE state IN ('planned', 'today')
      AND date_deadline < CURRENT_DATE;

    RETURN v_updated;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION notifications.update_activity_states() IS 'Actualiza estados de actividades segun fecha (ejecutar diariamente)';

-- Funcion para agregar follower automaticamente
CREATE OR REPLACE FUNCTION notifications.add_follower(
    p_tenant_id UUID,
    p_res_model VARCHAR(100),
    p_res_id UUID,
    p_partner_id UUID,
    p_partner_type VARCHAR(20) DEFAULT 'user',
    p_reason VARCHAR(100) DEFAULT 'manual'
)
RETURNS UUID AS $$
DECLARE
    v_follower_id UUID;
BEGIN
    INSERT INTO notifications.followers (tenant_id, res_model, res_id, partner_id, partner_type, reason)
    VALUES (p_tenant_id, p_res_model, p_res_id, p_partner_id, p_partner_type, p_reason)
    ON CONFLICT (tenant_id, res_model, res_id, partner_id) DO NOTHING
    RETURNING id INTO v_follower_id;

    -- Si ya existia, obtener el ID
    IF v_follower_id IS NULL THEN
        SELECT id INTO v_follower_id
        FROM notifications.followers
        WHERE tenant_id = p_tenant_id
          AND res_model = p_res_model
          AND res_id = p_res_id
          AND partner_id = p_partner_id;
    END IF;

    RETURN v_follower_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION notifications.add_follower IS 'Agrega un follower a un documento (idempotente)';

-- Funcion para registrar mensaje de tracking
CREATE OR REPLACE FUNCTION notifications.log_tracking_message(
    p_tenant_id UUID,
    p_res_model VARCHAR(100),
    p_res_id UUID,
    p_author_id UUID,
    p_tracking_values JSONB,
    p_body TEXT DEFAULT NULL
)
RETURNS UUID AS $$
DECLARE
    v_message_id UUID;
    v_subtype_id UUID;
BEGIN
    -- Obtener subtipo de tracking
    SELECT id INTO v_subtype_id
    FROM notifications.message_subtypes
    WHERE code = 'mt_tracking';

    INSERT INTO notifications.messages (
        tenant_id, res_model, res_id, message_type,
        subtype_id, author_id, body, tracking_values, is_internal
    )
    VALUES (
        p_tenant_id, p_res_model, p_res_id, 'notification',
        v_subtype_id, p_author_id, p_body, p_tracking_values, true
    )
    RETURNING id INTO v_message_id;

    RETURN v_message_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION notifications.log_tracking_message IS 'Registra un mensaje de tracking de cambios';

-- Funcion para crear actividad
CREATE OR REPLACE FUNCTION notifications.create_activity(
    p_tenant_id UUID,
    p_res_model VARCHAR(100),
    p_res_id UUID,
    p_activity_type_code VARCHAR(50),
    p_user_id UUID,
    p_date_deadline DATE DEFAULT NULL,
    p_summary VARCHAR(500) DEFAULT NULL,
    p_note TEXT DEFAULT NULL,
    p_created_by UUID DEFAULT NULL
)
RETURNS UUID AS $$
DECLARE
    v_activity_id UUID;
    v_activity_type_id UUID;
    v_default_days INTEGER;
BEGIN
    -- Obtener tipo de actividad
    SELECT id, default_days INTO v_activity_type_id, v_default_days
    FROM notifications.activity_types
    WHERE code = p_activity_type_code AND is_active = true;

    IF v_activity_type_id IS NULL THEN
        RAISE EXCEPTION 'Activity type % not found', p_activity_type_code;
    END IF;

    -- Calcular deadline si no se proporciono
    IF p_date_deadline IS NULL THEN
        p_date_deadline := CURRENT_DATE + v_default_days;
    END IF;

    -- Determinar estado inicial
    INSERT INTO notifications.activities (
        tenant_id, res_model, res_id, activity_type_id, user_id,
        date_deadline, summary, note, state, created_by
    )
    VALUES (
        p_tenant_id, p_res_model, p_res_id, v_activity_type_id, p_user_id,
        p_date_deadline, p_summary, p_note,
        CASE
            WHEN p_date_deadline < CURRENT_DATE THEN 'overdue'
            WHEN p_date_deadline = CURRENT_DATE THEN 'today'
            ELSE 'planned'
        END,
        COALESCE(p_created_by, get_current_user_id())
    )
    RETURNING id INTO v_activity_id;

    RETURN v_activity_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION notifications.create_activity IS 'Crea una nueva actividad programada';

-- Funcion para completar actividad
CREATE OR REPLACE FUNCTION notifications.complete_activity(
    p_activity_id UUID,
    p_feedback TEXT DEFAULT NULL
)
RETURNS BOOLEAN AS $$
BEGIN
    UPDATE notifications.activities
    SET state = 'done',
        date_done = NOW(),
        feedback = p_feedback
    WHERE id = p_activity_id
      AND state NOT IN ('done', 'canceled');

    RETURN FOUND;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION notifications.complete_activity IS 'Marca una actividad como completada';

-- ============================================
-- VISTAS UTILES
-- ============================================

-- Vista de actividades pendientes por usuario
CREATE VIEW notifications.v_pending_activities AS
SELECT
    a.id,
    a.tenant_id,
    a.res_model,
    a.res_id,
    at.code as activity_type_code,
    at.name as activity_type_name,
    at.icon,
    at.color,
    a.user_id,
    a.date_deadline,
    a.summary,
    a.note,
    a.state,
    CASE
        WHEN a.date_deadline < CURRENT_DATE THEN a.date_deadline - CURRENT_DATE
        ELSE 0
    END as days_overdue,
    a.created_at
FROM notifications.activities a
JOIN notifications.activity_types at ON at.id = a.activity_type_id
WHERE a.state NOT IN ('done', 'canceled')
ORDER BY a.date_deadline ASC;

COMMENT ON VIEW notifications.v_pending_activities IS 'Actividades pendientes con informacion de tipo';

-- Vista de historial de mensajes por documento
CREATE VIEW notifications.v_message_history AS
SELECT
    m.id,
    m.tenant_id,
    m.res_model,
    m.res_id,
    m.message_type,
    ms.code as subtype_code,
    ms.name as subtype_name,
    m.author_id,
    m.author_name,
    m.subject,
    m.body,
    m.tracking_values,
    m.is_internal,
    m.parent_id,
    m.created_at
FROM notifications.messages m
LEFT JOIN notifications.message_subtypes ms ON ms.id = m.subtype_id
ORDER BY m.created_at DESC;

COMMENT ON VIEW notifications.v_message_history IS 'Historial de mensajes formateado con subtipos';

-- ============================================
-- GRANTS ADICIONALES
-- ============================================
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA notifications TO mecanicas_user;
GRANT SELECT ON ALL TABLES IN SCHEMA notifications TO mecanicas_user;
