-- =============================================================
-- ARCHIVO: 02-auth-devices.sql
-- DESCRIPCION: Dispositivos, credenciales biometricas y sesiones
-- VERSION: 1.0.0
-- PROYECTO: ERP-Core V2
-- FECHA: 2026-01-10
-- =============================================================

-- =====================
-- TABLA: devices
-- Dispositivos registrados por usuario
-- =====================
CREATE TABLE IF NOT EXISTS auth.devices (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
    tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Identificacion del dispositivo
    device_uuid VARCHAR(100) NOT NULL,
    device_name VARCHAR(100),
    device_model VARCHAR(100),
    device_brand VARCHAR(50),

    -- Plataforma
    platform VARCHAR(20) NOT NULL, -- ios, android, web, desktop
    platform_version VARCHAR(20),
    app_version VARCHAR(20),

    -- Estado
    is_active BOOLEAN DEFAULT TRUE,
    is_trusted BOOLEAN DEFAULT FALSE,
    trust_level INTEGER DEFAULT 0, -- 0=none, 1=low, 2=medium, 3=high

    -- Biometricos habilitados
    biometric_enabled BOOLEAN DEFAULT FALSE,
    biometric_type VARCHAR(50), -- fingerprint, face_id, face_recognition

    -- Push notifications
    push_token TEXT,
    push_token_updated_at TIMESTAMPTZ,

    -- Ubicacion ultima conocida
    last_latitude DECIMAL(10, 8),
    last_longitude DECIMAL(11, 8),
    last_location_at TIMESTAMPTZ,

    -- Seguridad
    last_ip_address INET,
    last_user_agent TEXT,

    -- Registro
    first_seen_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    last_seen_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMPTZ,

    UNIQUE(user_id, device_uuid)
);

-- Indices para devices
CREATE INDEX IF NOT EXISTS idx_devices_user ON auth.devices(user_id);
CREATE INDEX IF NOT EXISTS idx_devices_tenant ON auth.devices(tenant_id);
CREATE INDEX IF NOT EXISTS idx_devices_uuid ON auth.devices(device_uuid);
CREATE INDEX IF NOT EXISTS idx_devices_platform ON auth.devices(platform);
CREATE INDEX IF NOT EXISTS idx_devices_active ON auth.devices(is_active) WHERE is_active = TRUE;

-- =====================
-- TABLA: biometric_credentials
-- Credenciales biometricas por dispositivo
-- =====================
CREATE TABLE IF NOT EXISTS auth.biometric_credentials (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    device_id UUID NOT NULL REFERENCES auth.devices(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,

    -- Tipo de biometrico
    biometric_type VARCHAR(50) NOT NULL, -- fingerprint, face_id, face_recognition, iris

    -- Credencial (public key para WebAuthn/FIDO2)
    credential_id TEXT NOT NULL,
    public_key TEXT NOT NULL,
    algorithm VARCHAR(20) DEFAULT 'ES256',

    -- Metadata
    credential_name VARCHAR(100), -- "Huella indice derecho", "Face ID iPhone"
    is_primary BOOLEAN DEFAULT FALSE,

    -- Estado
    is_active BOOLEAN DEFAULT TRUE,
    last_used_at TIMESTAMPTZ,
    use_count INTEGER DEFAULT 0,

    -- Seguridad
    failed_attempts INTEGER DEFAULT 0,
    locked_until TIMESTAMPTZ,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMPTZ,

    UNIQUE(device_id, credential_id)
);

-- Indices para biometric_credentials
CREATE INDEX IF NOT EXISTS idx_biometric_credentials_device ON auth.biometric_credentials(device_id);
CREATE INDEX IF NOT EXISTS idx_biometric_credentials_user ON auth.biometric_credentials(user_id);
CREATE INDEX IF NOT EXISTS idx_biometric_credentials_type ON auth.biometric_credentials(biometric_type);

-- =====================
-- TABLA: device_sessions
-- Sesiones activas por dispositivo
-- =====================
CREATE TABLE IF NOT EXISTS auth.device_sessions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    device_id UUID NOT NULL REFERENCES auth.devices(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
    tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Tokens
    access_token_hash VARCHAR(255) NOT NULL,
    refresh_token_hash VARCHAR(255),

    -- Metodo de autenticacion
    auth_method VARCHAR(50) NOT NULL, -- password, biometric, oauth, mfa

    -- Validez
    issued_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMPTZ NOT NULL,
    refresh_expires_at TIMESTAMPTZ,

    -- Estado
    is_active BOOLEAN DEFAULT TRUE,
    revoked_at TIMESTAMPTZ,
    revoked_reason VARCHAR(100),

    -- Ubicacion
    ip_address INET,
    user_agent TEXT,
    latitude DECIMAL(10, 8),
    longitude DECIMAL(11, 8),

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Indices para device_sessions
CREATE INDEX IF NOT EXISTS idx_device_sessions_device ON auth.device_sessions(device_id);
CREATE INDEX IF NOT EXISTS idx_device_sessions_user ON auth.device_sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_device_sessions_tenant ON auth.device_sessions(tenant_id);
CREATE INDEX IF NOT EXISTS idx_device_sessions_token ON auth.device_sessions(access_token_hash);
CREATE INDEX IF NOT EXISTS idx_device_sessions_active ON auth.device_sessions(is_active, expires_at) WHERE is_active = TRUE;

-- =====================
-- TABLA: device_activity_log
-- Log de actividad de dispositivos
-- =====================
CREATE TABLE IF NOT EXISTS auth.device_activity_log (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    device_id UUID NOT NULL REFERENCES auth.devices(id) ON DELETE CASCADE,
    user_id UUID REFERENCES auth.users(id),

    -- Actividad
    activity_type VARCHAR(50) NOT NULL, -- login, logout, biometric_auth, location_update, app_open
    activity_status VARCHAR(20) NOT NULL, -- success, failed, blocked

    -- Detalles
    details JSONB DEFAULT '{}',

    -- Ubicacion
    ip_address INET,
    latitude DECIMAL(10, 8),
    longitude DECIMAL(11, 8),

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Indices para device_activity_log
CREATE INDEX IF NOT EXISTS idx_device_activity_device ON auth.device_activity_log(device_id);
CREATE INDEX IF NOT EXISTS idx_device_activity_user ON auth.device_activity_log(user_id);
CREATE INDEX IF NOT EXISTS idx_device_activity_type ON auth.device_activity_log(activity_type);
CREATE INDEX IF NOT EXISTS idx_device_activity_created ON auth.device_activity_log(created_at DESC);

-- Particionar por fecha para mejor rendimiento
-- CREATE TABLE auth.device_activity_log_y2026m01 PARTITION OF auth.device_activity_log
--     FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');

-- =====================
-- RLS POLICIES
-- =====================
ALTER TABLE auth.devices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_devices ON auth.devices
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

ALTER TABLE auth.biometric_credentials ENABLE ROW LEVEL SECURITY;
CREATE POLICY user_own_biometrics ON auth.biometric_credentials
    USING (user_id = current_setting('app.current_user_id', true)::uuid);

ALTER TABLE auth.device_sessions ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_sessions ON auth.device_sessions
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

ALTER TABLE auth.device_activity_log ENABLE ROW LEVEL SECURITY;
CREATE POLICY device_owner_activity ON auth.device_activity_log
    USING (device_id IN (
        SELECT id FROM auth.devices
        WHERE tenant_id = current_setting('app.current_tenant_id', true)::uuid
    ));

-- =====================
-- FUNCIONES
-- =====================

-- Funcion para actualizar last_seen_at del dispositivo
CREATE OR REPLACE FUNCTION auth.update_device_last_seen()
RETURNS TRIGGER AS $$
BEGIN
    UPDATE auth.devices
    SET last_seen_at = CURRENT_TIMESTAMP
    WHERE id = NEW.device_id;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Trigger para actualizar last_seen_at cuando hay actividad
CREATE TRIGGER trg_update_device_last_seen
    AFTER INSERT ON auth.device_activity_log
    FOR EACH ROW
    EXECUTE FUNCTION auth.update_device_last_seen();

-- Funcion para limpiar sesiones expiradas
CREATE OR REPLACE FUNCTION auth.cleanup_expired_sessions()
RETURNS INTEGER AS $$
DECLARE
    deleted_count INTEGER;
BEGIN
    DELETE FROM auth.device_sessions
    WHERE expires_at < CURRENT_TIMESTAMP
    AND is_active = FALSE;

    GET DIAGNOSTICS deleted_count = ROW_COUNT;
    RETURN deleted_count;
END;
$$ LANGUAGE plpgsql;

-- =====================
-- COMENTARIOS DE TABLAS
-- =====================
COMMENT ON TABLE auth.devices IS 'Dispositivos registrados por usuario (moviles, web, desktop)';
COMMENT ON TABLE auth.biometric_credentials IS 'Credenciales biometricas registradas por dispositivo (huella, face ID)';
COMMENT ON TABLE auth.device_sessions IS 'Sesiones activas por dispositivo con tokens';
COMMENT ON TABLE auth.device_activity_log IS 'Log de actividad de dispositivos para auditoria';
