-- ============================================================================
-- SCHEMA: users
-- TABLE: users
-- DESCRIPTION: Sistema de usuarios con autenticacion y perfiles
-- VERSION: 1.0.0
-- CREATED: 2026-01-10
-- ============================================================================

-- Crear schema si no existe
CREATE SCHEMA IF NOT EXISTS users;

-- Enum para estado de usuario
DO $$ BEGIN
    CREATE TYPE users.user_status AS ENUM (
        'pending',      -- Pendiente de verificacion de email
        'active',       -- Activo y verificado
        'suspended',    -- Suspendido temporalmente
        'banned',       -- Baneado permanentemente
        'deleted'       -- Eliminado (soft delete)
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Tabla principal de Usuarios
CREATE TABLE IF NOT EXISTS users.users (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,

    -- Credenciales
    email VARCHAR(255) NOT NULL,
    password_hash VARCHAR(255) NOT NULL,

    -- Perfil
    first_name VARCHAR(100),
    last_name VARCHAR(100),
    display_name VARCHAR(100),
    avatar_url TEXT,
    phone VARCHAR(20),

    -- Estado
    status users.user_status NOT NULL DEFAULT 'pending',
    is_owner BOOLEAN NOT NULL DEFAULT FALSE,  -- Owner del tenant

    -- Verificaciones
    email_verified BOOLEAN NOT NULL DEFAULT FALSE,
    email_verified_at TIMESTAMPTZ,
    phone_verified BOOLEAN NOT NULL DEFAULT FALSE,
    phone_verified_at TIMESTAMPTZ,

    -- MFA
    mfa_enabled BOOLEAN NOT NULL DEFAULT FALSE,
    mfa_secret VARCHAR(255),  -- TOTP secret (encrypted)
    mfa_backup_codes JSONB,   -- Array de backup codes hasheados

    -- Seguridad
    password_changed_at TIMESTAMPTZ DEFAULT NOW(),
    failed_login_attempts INTEGER NOT NULL DEFAULT 0,
    locked_until TIMESTAMPTZ,
    last_login_at TIMESTAMPTZ,
    last_login_ip INET,

    -- Preferencias (JSONB flexible)
    preferences JSONB NOT NULL DEFAULT '{
        "theme": "dark",
        "language": "es",
        "notifications": {
            "email": true,
            "push": true,
            "sms": false
        }
    }'::JSONB,

    -- Metadata adicional
    metadata JSONB DEFAULT '{}'::JSONB,

    -- Soft delete
    deleted_at TIMESTAMPTZ,

    -- Timestamps
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- Constraints
    CONSTRAINT users_unique_email_per_tenant UNIQUE (tenant_id, email)
);

COMMENT ON TABLE users.users IS
'Usuarios de la plataforma, aislados por tenant';

COMMENT ON COLUMN users.users.is_owner IS
'Indica si es el owner del tenant (tiene todos los permisos)';

COMMENT ON COLUMN users.users.password_hash IS
'Hash bcrypt del password (cost 10)';

-- Indices
CREATE INDEX IF NOT EXISTS idx_users_tenant
    ON users.users(tenant_id);

CREATE INDEX IF NOT EXISTS idx_users_email
    ON users.users(email);

CREATE INDEX IF NOT EXISTS idx_users_status
    ON users.users(status) WHERE deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_users_owner
    ON users.users(tenant_id, is_owner) WHERE is_owner = TRUE;

CREATE INDEX IF NOT EXISTS idx_users_last_login
    ON users.users(last_login_at DESC);

-- Trigger para updated_at
CREATE OR REPLACE FUNCTION users.update_user_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS user_updated_at ON users.users;
CREATE TRIGGER user_updated_at
    BEFORE UPDATE ON users.users
    FOR EACH ROW
    EXECUTE FUNCTION users.update_user_timestamp();

-- Funcion para limpiar failed_login_attempts despues de login exitoso
CREATE OR REPLACE FUNCTION users.clear_failed_logins()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.last_login_at IS DISTINCT FROM OLD.last_login_at THEN
        NEW.failed_login_attempts := 0;
        NEW.locked_until := NULL;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS user_clear_failed_logins ON users.users;
CREATE TRIGGER user_clear_failed_logins
    BEFORE UPDATE ON users.users
    FOR EACH ROW
    EXECUTE FUNCTION users.clear_failed_logins();

-- RLS Policy para multi-tenancy
ALTER TABLE users.users ENABLE ROW LEVEL SECURITY;

CREATE POLICY users_tenant_isolation ON users.users
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

-- Grants
GRANT SELECT, INSERT, UPDATE ON users.users TO trading_app;
GRANT SELECT ON users.users TO trading_readonly;
