-- ServiceManagerWeb - Esquema de Base de Datos
-- Sistema multi-tenant de Mesa de Ayuda B2B
-- PostgreSQL 15+

-- ===================================
-- DOMINIO: TENANTS (Multi-tenancy)
-- ===================================

CREATE TABLE tenants (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(255) NOT NULL,
    slug VARCHAR(100) UNIQUE NOT NULL,
    domain VARCHAR(255),
    logo_url VARCHAR(500),
    contact_email VARCHAR(320),
    contact_phone VARCHAR(20),
    address TEXT,
    timezone VARCHAR(50) DEFAULT 'UTC',
    locale VARCHAR(10) DEFAULT 'es-ES',
    
    -- Settings
    max_users INTEGER DEFAULT 50,
    max_storage_mb INTEGER DEFAULT 1024,
    allowed_file_types TEXT[] DEFAULT ARRAY['pdf','jpg','jpeg','png','doc','docx','xls','xlsx','txt'],
    
    -- Status
    status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'inactive')),
    
    -- Timestamps
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    
    CONSTRAINT tenants_slug_format CHECK (slug ~ '^[a-z0-9-]+$')
);

-- Índices para tenants
CREATE INDEX idx_tenants_slug ON tenants(slug);
CREATE INDEX idx_tenants_status ON tenants(status);
CREATE INDEX idx_tenants_domain ON tenants(domain);

-- ===================================
-- DOMINIO: AUTH (Autenticación)
-- ===================================

CREATE TYPE user_role_enum AS ENUM (
    'ADMIN',           -- Control total (staff interno)
    'SUPPORT_MANAGER', -- Gestión de equipos y SLAs
    'AGENT',           -- Atención de tickets
    'AUDITOR',         -- Solo lectura para auditoría
    'CLIENT_ADMIN',    -- Admin de organización cliente
    'CLIENT_USER'      -- Usuario final cliente
);

CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    
    -- Básicos
    email VARCHAR(320) NOT NULL,
    first_name VARCHAR(100) NOT NULL,
    last_name VARCHAR(100) NOT NULL,
    avatar_url VARCHAR(500),
    
    -- Auth
    password_hash VARCHAR(255) NOT NULL,
    role user_role_enum NOT NULL,
    
    -- 2FA (opcional para staff interno)
    totp_secret VARCHAR(32),
    totp_enabled BOOLEAN DEFAULT FALSE,
    backup_codes TEXT[], -- códigos de recuperación
    
    -- Status
    is_active BOOLEAN DEFAULT TRUE,
    email_verified BOOLEAN DEFAULT FALSE,
    last_login TIMESTAMP WITH TIME ZONE,
    last_activity TIMESTAMP WITH TIME ZONE,
    
    -- Preferences
    language VARCHAR(10) DEFAULT 'es',
    timezone VARCHAR(50) DEFAULT 'UTC',
    notifications_email BOOLEAN DEFAULT TRUE,
    
    -- Timestamps
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    
    UNIQUE(tenant_id, email)
);

-- Índices para users
CREATE INDEX idx_users_tenant_id ON users(tenant_id);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_role ON users(role);
CREATE INDEX idx_users_active ON users(is_active);
CREATE INDEX idx_users_tenant_email ON users(tenant_id, email);

-- Tabla para refresh tokens
CREATE TABLE refresh_tokens (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    token_hash VARCHAR(255) NOT NULL,
    device_info VARCHAR(500),
    ip_address INET,
    expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
    revoked BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Índices para refresh_tokens
CREATE INDEX idx_refresh_tokens_user_id ON refresh_tokens(user_id);
CREATE INDEX idx_refresh_tokens_hash ON refresh_tokens(token_hash);
CREATE INDEX idx_refresh_tokens_expires ON refresh_tokens(expires_at);

-- ===================================
-- DOMINIO: TICKETS (Core del negocio)
-- ===================================

CREATE TYPE ticket_status_enum AS ENUM (
    'NEW',
    'TRIAGE',
    'IN_PROGRESS',
    'WAITING_CUSTOMER',
    'RESOLVED',
    'CLOSED',
    'REOPENED'
);

CREATE TYPE ticket_priority_enum AS ENUM (
    'LOW',
    'MEDIUM', 
    'HIGH',
    'URGENT'
);

-- Categorías de tickets por tenant
CREATE TABLE ticket_categories (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    color VARCHAR(7), -- hex color
    sla_response_hours INTEGER DEFAULT 24,
    sla_resolution_hours INTEGER DEFAULT 72,
    auto_assign_to UUID REFERENCES users(id),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    
    UNIQUE(tenant_id, name)
);

-- Sistemas afectados por tenant
CREATE TABLE affected_systems (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    
    UNIQUE(tenant_id, name)
);

-- Tickets principales
CREATE TABLE tickets (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    ticket_number VARCHAR(20) NOT NULL, -- formato: TKT-2024-000001
    
    -- Básicos
    subject VARCHAR(255) NOT NULL,
    description TEXT NOT NULL,
    
    -- Clasificación
    category_id UUID REFERENCES ticket_categories(id),
    priority ticket_priority_enum DEFAULT 'MEDIUM',
    affected_system_id UUID REFERENCES affected_systems(id),
    
    -- Asignación
    created_by UUID NOT NULL REFERENCES users(id),
    assigned_to UUID REFERENCES users(id),
    
    -- Estado
    status ticket_status_enum DEFAULT 'NEW',
    
    -- SLA tracking
    sla_response_due TIMESTAMP WITH TIME ZONE,
    sla_resolution_due TIMESTAMP WITH TIME ZONE,
    first_response_at TIMESTAMP WITH TIME ZONE,
    resolved_at TIMESTAMP WITH TIME ZONE,
    
    -- CSAT (Customer Satisfaction)
    rating INTEGER CHECK (rating >= 1 AND rating <= 5),
    rating_comment TEXT,
    rated_at TIMESTAMP WITH TIME ZONE,
    
    -- Timestamps
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    
    UNIQUE(tenant_id, ticket_number)
);

-- Índices para tickets
CREATE INDEX idx_tickets_tenant_id ON tickets(tenant_id);
CREATE INDEX idx_tickets_number ON tickets(ticket_number);
CREATE INDEX idx_tickets_created_by ON tickets(created_by);
CREATE INDEX idx_tickets_assigned_to ON tickets(assigned_to);
CREATE INDEX idx_tickets_status ON tickets(status);
CREATE INDEX idx_tickets_priority ON tickets(priority);
CREATE INDEX idx_tickets_category ON tickets(category_id);
CREATE INDEX idx_tickets_sla_response ON tickets(sla_response_due);
CREATE INDEX idx_tickets_sla_resolution ON tickets(sla_resolution_due);
CREATE INDEX idx_tickets_created_at ON tickets(created_at);

-- Comentarios en tickets
CREATE TABLE ticket_comments (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    ticket_id UUID NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
    author_id UUID NOT NULL REFERENCES users(id),
    
    content TEXT NOT NULL,
    is_internal BOOLEAN DEFAULT FALSE, -- solo visible para staff interno
    
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Índices para comentarios
CREATE INDEX idx_ticket_comments_ticket_id ON ticket_comments(ticket_id);
CREATE INDEX idx_ticket_comments_author_id ON ticket_comments(author_id);
CREATE INDEX idx_ticket_comments_created_at ON ticket_comments(created_at);

-- Adjuntos en tickets/comentarios
CREATE TABLE ticket_attachments (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    ticket_id UUID NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
    comment_id UUID REFERENCES ticket_comments(id) ON DELETE CASCADE,
    uploaded_by UUID NOT NULL REFERENCES users(id),
    
    -- Archivo
    filename VARCHAR(255) NOT NULL,
    original_filename VARCHAR(255) NOT NULL,
    mime_type VARCHAR(100) NOT NULL,
    file_size INTEGER NOT NULL, -- bytes
    file_path VARCHAR(500) NOT NULL, -- path en storage
    
    -- Checksums para integridad
    md5_hash VARCHAR(32),
    sha256_hash VARCHAR(64),
    
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Índices para adjuntos
CREATE INDEX idx_ticket_attachments_ticket_id ON ticket_attachments(ticket_id);
CREATE INDEX idx_ticket_attachments_comment_id ON ticket_attachments(comment_id);
CREATE INDEX idx_ticket_attachments_uploaded_by ON ticket_attachments(uploaded_by);

-- Historial de cambios de estado
CREATE TABLE ticket_status_history (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    ticket_id UUID NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
    changed_by UUID NOT NULL REFERENCES users(id),
    
    old_status ticket_status_enum,
    new_status ticket_status_enum NOT NULL,
    old_assigned_to UUID REFERENCES users(id),
    new_assigned_to UUID REFERENCES users(id),
    
    comment TEXT,
    
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Índices para historial de estados
CREATE INDEX idx_ticket_status_history_ticket_id ON ticket_status_history(ticket_id);
CREATE INDEX idx_ticket_status_history_changed_by ON ticket_status_history(changed_by);
CREATE INDEX idx_ticket_status_history_created_at ON ticket_status_history(created_at);

-- ===================================
-- DOMINIO: NOTIFICATIONS (Comunicaciones)
-- ===================================

-- Templates de email
CREATE TABLE email_templates (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID REFERENCES tenants(id), -- NULL = template global
    
    name VARCHAR(100) NOT NULL,
    subject_template TEXT NOT NULL,
    body_template TEXT NOT NULL,
    template_type VARCHAR(50) NOT NULL, -- ticket_created, ticket_assigned, etc.
    
    -- Variables disponibles en formato JSON
    available_variables JSONB,
    
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Índices para templates
CREATE INDEX idx_email_templates_tenant_id ON email_templates(tenant_id);
CREATE INDEX idx_email_templates_type ON email_templates(template_type);

-- Log de notificaciones enviadas
CREATE TABLE notification_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants(id),
    
    recipient_email VARCHAR(320) NOT NULL,
    subject VARCHAR(500) NOT NULL,
    template_type VARCHAR(50),
    
    -- Metadata
    ticket_id UUID REFERENCES tickets(id),
    user_id UUID REFERENCES users(id),
    
    -- Estado del envío
    status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'sent', 'failed', 'bounced')),
    error_message TEXT,
    
    -- Proveedor (ej: sendgrid, ses, smtp)
    provider VARCHAR(50),
    external_id VARCHAR(255), -- ID del proveedor
    
    sent_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Índices para logs de notificaciones
CREATE INDEX idx_notification_logs_tenant_id ON notification_logs(tenant_id);
CREATE INDEX idx_notification_logs_recipient ON notification_logs(recipient_email);
CREATE INDEX idx_notification_logs_ticket_id ON notification_logs(ticket_id);
CREATE INDEX idx_notification_logs_status ON notification_logs(status);
CREATE INDEX idx_notification_logs_created_at ON notification_logs(created_at);

-- ===================================
-- DOMINIO: AUDIT (Bitácora)
-- ===================================

CREATE TABLE audit_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants(id),
    user_id UUID REFERENCES users(id), -- NULL para acciones del sistema
    
    -- Acción
    action VARCHAR(100) NOT NULL, -- ej: user.login, ticket.create, ticket.assign
    resource_type VARCHAR(50) NOT NULL, -- user, ticket, comment, etc.
    resource_id UUID,
    
    -- Contexto
    ip_address INET,
    user_agent TEXT,
    correlation_id UUID, -- para rastrear requests
    
    -- Cambios (antes/después en JSON)
    old_values JSONB,
    new_values JSONB,
    
    -- Metadata adicional
    metadata JSONB,
    
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Índices para audit logs
CREATE INDEX idx_audit_logs_tenant_id ON audit_logs(tenant_id);
CREATE INDEX idx_audit_logs_user_id ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_action ON audit_logs(action);
CREATE INDEX idx_audit_logs_correlation_id ON audit_logs(correlation_id);
CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at);

-- Índices compuestos para queries comunes de auditoría
CREATE INDEX idx_audit_logs_tenant_action ON audit_logs(tenant_id, action);
CREATE INDEX idx_audit_logs_resource ON audit_logs(resource_type, resource_id);
CREATE INDEX idx_audit_logs_user_created ON audit_logs(user_id, created_at);

-- ===================================
-- FUNCIONES Y TRIGGERS
-- ===================================

-- Función para actualizar updated_at automáticamente
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ language 'plpgsql';

-- Triggers para updated_at
CREATE TRIGGER update_tenants_updated_at BEFORE UPDATE ON tenants 
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users 
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_tickets_updated_at BEFORE UPDATE ON tickets 
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_ticket_comments_updated_at BEFORE UPDATE ON ticket_comments 
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_email_templates_updated_at BEFORE UPDATE ON email_templates 
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

-- ===================================
-- DATOS INICIALES (SEEDS)
-- ===================================

-- Tenant de ejemplo para desarrollo
INSERT INTO tenants (name, slug, contact_email) VALUES 
('Aduanasoft Demo', 'aduanasoft-demo', 'demo@aduanasoft.com');

-- Usuario admin por defecto (password: [REDACTED]
-- Hash Argon2id generado con m=65536,t=3,p=4
INSERT INTO users (tenant_id, email, first_name, last_name, password_hash, role, is_active, email_verified) 
SELECT 
    id,
    'admin@aduanasoft.com',
    'Admin', 
    'Sistema',
    '$argon2id$v=19$m=65536,t=3,p=4$wpjz/t+bM4bQmtM6B6A0pg$ELwnGUL4S1Y6tywp0LS6cre0bvWEoVuJ845spZ9Z9IQ',
    'ADMIN',
    true,
    true
FROM tenants WHERE slug = 'aduanasoft-demo'
ON CONFLICT (tenant_id, email) DO NOTHING;

-- Categorías por defecto
INSERT INTO ticket_categories (tenant_id, name, description, sla_response_hours, sla_resolution_hours)
SELECT 
    id,
    'Soporte Técnico',
    'Problemas técnicos generales',
    2,
    24
FROM tenants WHERE slug = 'aduanasoft-demo'
UNION ALL
SELECT 
    id,
    'Consulta Comercial',
    'Preguntas sobre productos y servicios',
    4,
    48
FROM tenants WHERE slug = 'aduanasoft-demo'
UNION ALL
SELECT 
    id,
    'Incidente Crítico',
    'Problemas que afectan operaciones',
    1,
    8
FROM tenants WHERE slug = 'aduanasoft-demo';

-- Sistemas afectados por defecto
INSERT INTO affected_systems (tenant_id, name, description)
SELECT 
    id,
    'Plataforma Web',
    'Sistema web principal'
FROM tenants WHERE slug = 'aduanasoft-demo'
UNION ALL
SELECT 
    id,
    'API',
    'Servicios de API'
FROM tenants WHERE slug = 'aduanasoft-demo'
UNION ALL
SELECT 
    id,
    'Base de Datos',
    'Sistemas de almacenamiento'
FROM tenants WHERE slug = 'aduanasoft-demo';

-- Templates de email básicos
INSERT INTO email_templates (name, subject_template, body_template, template_type, available_variables)
VALUES 
(
    'Ticket Creado',
    'Nuevo ticket #{{ticket_number}}: {{subject}}',
    'Hola {{user_name}},\n\nSe ha creado un nuevo ticket:\n\nNúmero: #{{ticket_number}}\nAsunto: {{subject}}\nPrioridad: {{priority}}\n\nPuedes ver los detalles en: {{ticket_url}}\n\nSaludos,\nEquipo de Soporte',
    'ticket_created',
    '{"ticket_number": "string", "subject": "string", "priority": "string", "user_name": "string", "ticket_url": "string"}'
),
(
    'Ticket Asignado',
    'Ticket #{{ticket_number}} asignado a ti',
    'Hola {{agent_name}},\n\nSe te ha asignado el ticket:\n\nNúmero: #{{ticket_number}}\nAsunto: {{subject}}\nCliente: {{customer_name}}\nPrioridad: {{priority}}\n\nPuedes verlo en: {{ticket_url}}\n\nSaludos,\nSistema de Tickets',
    'ticket_assigned',
    '{"ticket_number": "string", "subject": "string", "agent_name": "string", "customer_name": "string", "priority": "string", "ticket_url": "string"}'
);

-- ===================================
-- COMENTARIOS Y DOCUMENTACIÓN
-- ===================================

COMMENT ON TABLE tenants IS 'Organizaciones cliente en el sistema multi-tenant';
COMMENT ON TABLE users IS 'Usuarios del sistema (internos y clientes)';
COMMENT ON TABLE tickets IS 'Tickets de soporte - core del negocio';
COMMENT ON TABLE ticket_comments IS 'Comentarios en tickets';
COMMENT ON TABLE ticket_attachments IS 'Archivos adjuntos en tickets';
COMMENT ON TABLE audit_logs IS 'Bitácora de acciones para auditoría y compliance';