-- ============================================
-- Migration: V20260120_001
-- Description: Migrate auth.sessions structure
-- Changes:
--   - Rename session_token -> token_hash (VARCHAR(64) -> VARCHAR(255))
--   - Change is_active (BOOLEAN) -> status (ENUM)
--   - Add device_name, browser, os, location columns
--   - Add revoked_at, revoked_reason columns
-- ============================================

-- UP Migration
BEGIN;

-- ============================================
-- 1. Check if we need to migrate (legacy structure)
-- ============================================

DO $$
DECLARE
    has_session_token BOOLEAN;
    has_is_active BOOLEAN;
    has_status BOOLEAN;
BEGIN
    -- Check for legacy column session_token
    SELECT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'auth'
        AND table_name = 'sessions'
        AND column_name = 'session_token'
    ) INTO has_session_token;

    -- Check for legacy column is_active
    SELECT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'auth'
        AND table_name = 'sessions'
        AND column_name = 'is_active'
    ) INTO has_is_active;

    -- Check if status column already exists
    SELECT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'auth'
        AND table_name = 'sessions'
        AND column_name = 'status'
    ) INTO has_status;

    -- Store results in temp table for use in subsequent statements
    CREATE TEMP TABLE _migration_flags (
        flag_name VARCHAR(50) PRIMARY KEY,
        flag_value BOOLEAN
    ) ON COMMIT DROP;

    INSERT INTO _migration_flags VALUES
        ('has_session_token', has_session_token),
        ('has_is_active', has_is_active),
        ('has_status', has_status);
END $$;

-- ============================================
-- 2. Ensure session_status ENUM exists
-- ============================================

DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'session_status' AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'auth')) THEN
        CREATE TYPE auth.session_status AS ENUM ('active', 'expired', 'revoked');
    END IF;
END $$;

-- ============================================
-- 3. Rename session_token -> token_hash (if legacy)
-- ============================================

DO $$
BEGIN
    IF EXISTS (SELECT 1 FROM _migration_flags WHERE flag_name = 'has_session_token' AND flag_value = TRUE) THEN
        -- Drop old index if exists
        DROP INDEX IF EXISTS auth.idx_sessions_token;

        -- Rename column
        ALTER TABLE auth.sessions RENAME COLUMN session_token TO token_hash;

        -- Expand varchar size (64 -> 255)
        ALTER TABLE auth.sessions ALTER COLUMN token_hash TYPE VARCHAR(255);

        -- Recreate index
        CREATE INDEX idx_sessions_token ON auth.sessions(token_hash);

        RAISE NOTICE 'Renamed session_token to token_hash and expanded to VARCHAR(255)';
    END IF;
END $$;

-- ============================================
-- 4. Convert is_active (BOOLEAN) -> status (ENUM)
-- ============================================

DO $$
DECLARE
    has_is_active BOOLEAN;
    has_status BOOLEAN;
BEGIN
    SELECT flag_value INTO has_is_active FROM _migration_flags WHERE flag_name = 'has_is_active';
    SELECT flag_value INTO has_status FROM _migration_flags WHERE flag_name = 'has_status';

    IF has_is_active AND NOT has_status THEN
        -- Add status column
        ALTER TABLE auth.sessions ADD COLUMN status auth.session_status;

        -- Migrate data: is_active = true -> 'active', is_active = false -> 'revoked'
        UPDATE auth.sessions
        SET status = CASE
            WHEN is_active = true THEN 'active'::auth.session_status
            ELSE 'revoked'::auth.session_status
        END;

        -- Mark expired sessions
        UPDATE auth.sessions
        SET status = 'expired'::auth.session_status
        WHERE expires_at < NOW() AND status = 'active';

        -- Set NOT NULL and default
        ALTER TABLE auth.sessions ALTER COLUMN status SET NOT NULL;
        ALTER TABLE auth.sessions ALTER COLUMN status SET DEFAULT 'active';

        -- Drop old column
        ALTER TABLE auth.sessions DROP COLUMN is_active;

        -- Update indexes to use status instead of is_active
        DROP INDEX IF EXISTS auth.idx_sessions_user;
        DROP INDEX IF EXISTS auth.idx_sessions_tenant;
        DROP INDEX IF EXISTS auth.idx_sessions_expires;

        CREATE INDEX idx_sessions_user ON auth.sessions(user_id) WHERE status = 'active';
        CREATE INDEX idx_sessions_tenant ON auth.sessions(tenant_id) WHERE status = 'active';
        CREATE INDEX idx_sessions_expires ON auth.sessions(expires_at) WHERE status = 'active';

        RAISE NOTICE 'Converted is_active to status enum';
    END IF;
END $$;

-- ============================================
-- 5. Add new columns if they don't exist
-- ============================================

-- device_name
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'auth' AND table_name = 'sessions' AND column_name = 'device_name'
    ) THEN
        ALTER TABLE auth.sessions ADD COLUMN device_name VARCHAR(200);
        RAISE NOTICE 'Added device_name column';
    END IF;
END $$;

-- browser
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'auth' AND table_name = 'sessions' AND column_name = 'browser'
    ) THEN
        ALTER TABLE auth.sessions ADD COLUMN browser VARCHAR(100);
        RAISE NOTICE 'Added browser column';
    END IF;
END $$;

-- os
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'auth' AND table_name = 'sessions' AND column_name = 'os'
    ) THEN
        ALTER TABLE auth.sessions ADD COLUMN os VARCHAR(100);
        RAISE NOTICE 'Added os column';
    END IF;
END $$;

-- location
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'auth' AND table_name = 'sessions' AND column_name = 'location'
    ) THEN
        ALTER TABLE auth.sessions ADD COLUMN location VARCHAR(200);
        RAISE NOTICE 'Added location column';
    END IF;
END $$;

-- revoked_at
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'auth' AND table_name = 'sessions' AND column_name = 'revoked_at'
    ) THEN
        ALTER TABLE auth.sessions ADD COLUMN revoked_at TIMESTAMPTZ;
        RAISE NOTICE 'Added revoked_at column';
    END IF;
END $$;

-- revoked_reason
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'auth' AND table_name = 'sessions' AND column_name = 'revoked_reason'
    ) THEN
        ALTER TABLE auth.sessions ADD COLUMN revoked_reason VARCHAR(100);
        RAISE NOTICE 'Added revoked_reason column';
    END IF;
END $$;

-- ============================================
-- 6. Update cleanup function to use status
-- ============================================

CREATE OR REPLACE FUNCTION auth.cleanup_expired_sessions()
RETURNS INTEGER AS $$
DECLARE
    deleted_count INTEGER;
BEGIN
    WITH deleted AS (
        DELETE FROM auth.sessions
        WHERE expires_at < NOW() - INTERVAL '7 days'
        OR (status = 'revoked' AND revoked_at < NOW() - INTERVAL '30 days')
        RETURNING *
    )
    SELECT COUNT(*) INTO deleted_count FROM deleted;

    RETURN deleted_count;
END;
$$ LANGUAGE plpgsql;

-- ============================================
-- 7. Update comments
-- ============================================

COMMENT ON COLUMN auth.sessions.token_hash IS 'SHA256 hash of session token for secure storage';
COMMENT ON COLUMN auth.sessions.status IS 'Session status: active, expired, revoked';
COMMENT ON COLUMN auth.sessions.device_name IS 'Human-readable device name';
COMMENT ON COLUMN auth.sessions.browser IS 'Browser name and version';
COMMENT ON COLUMN auth.sessions.os IS 'Operating system name and version';
COMMENT ON COLUMN auth.sessions.location IS 'Geographic location (City, Country)';
COMMENT ON COLUMN auth.sessions.revoked_at IS 'Timestamp when session was revoked';
COMMENT ON COLUMN auth.sessions.revoked_reason IS 'Reason for revocation: logout, security, password_change, admin';

COMMIT;
