-- Migration 192: tradein_users + tradein_sessions — DB-backed auth (issue #2551, эпик #2549)
--
-- WHY:
--   Trade-in auth сейчас держится на legacy Caddy basic-auth fallback (см. auth/roles.yaml,
--   упомянут в 191_account_quota_unlimited_flag.sql как "хардкод username в коде"). Эпик #2549
--   переводит auth на DB-backed модель: пользователи + сессии как данные, роли admin/manager/
--   employee с иерархией manager -> employee. Эта миграция — только схема (Foundation),
--   без seed-данных (seed — отдельная задача #2557) и без Python-кода (backend wiring — отдельно).
--
-- WHAT:
--   1. tradein_users — identity + role + org-иерархия.
--      - password_hash NULL допустим: переходный период, когда логин ещё идёт через
--        legacy Caddy fallback, а не через password verify в приложении.
--      - role CHECK ('admin','manager','employee') — три уровня доступа.
--      - manager_id — self-FK, ON DELETE SET NULL (увольнение/удаление manager'а не должно
--        каскадно сносить его employees, они просто остаются без привязки).
--      - CHECK role_manager_hierarchy: admin/manager обязаны иметь manager_id IS NULL
--        (это top-level роли, у них нет "начальника" в этой модели); employee — manager_id
--        любой, включая NULL (свободный слот employee без организации допустим).
--   2. tradein_sessions — токен-based сессии, привязаны к user_id, ON DELETE CASCADE
--      (удалили пользователя — его сессии теряют смысл, каскадная очистка корректна).
--      last_seen_at отдельно от created_at — для idle-timeout / активности сессии.
--   3. Индексы: expires_at (уборка протухших сессий), user_id (список сессий юзера),
--      partial на manager_id (иерархия) — WHERE manager_id IS NOT NULL, т.к. большинство
--      admin/manager строк это NULL и не участвуют в lookup "employees этого manager'а".
--
-- IDEMPOTENCY:
--   CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS. Повторный прогон — no-op.
--   CHECK-констрейнты добавлены inline в CREATE TABLE (не через ALTER) — при повторном
--   запуске CREATE TABLE IF NOT EXISTS не выполнится вообще, констрейнт не задублируется.
--
-- Dependencies: нет (новые таблицы, ничего существующего не меняем).
-- Deploy order: эта миграция — Foundation эпика #2549. Seed (#2557) и backend auth-код —
--   отдельные PR'ы ПОСЛЕ этой (SQL-схема первой, см. .claude/rules/sql.md "Migration order").

BEGIN;

CREATE TABLE IF NOT EXISTS tradein_users (
    id             bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    username       text NOT NULL UNIQUE,
    password_hash  text NULL,
    role           text NOT NULL CHECK (role IN ('admin', 'manager', 'employee')),
    manager_id     bigint NULL REFERENCES tradein_users(id) ON DELETE SET NULL,
    display_name   text NULL,
    org_name       text NULL,
    email          text NULL,
    is_active      boolean NOT NULL DEFAULT true,
    created_at     timestamptz NOT NULL DEFAULT now(),
    updated_at     timestamptz NOT NULL DEFAULT now(),
    CONSTRAINT tradein_users_role_manager_hierarchy_ck CHECK (
        role NOT IN ('admin', 'manager') OR manager_id IS NULL
    )
);

COMMENT ON TABLE tradein_users IS
    'Trade-in DB-backed auth — пользователи (issue #2551, эпик #2549). password_hash NULL '
    'допустим в переходный период (логин через legacy Caddy fallback). Seed — отдельно (#2557).';
COMMENT ON COLUMN tradein_users.password_hash IS
    'NULL = логин только через legacy Caddy basic-auth fallback, не через password verify.';
COMMENT ON COLUMN tradein_users.manager_id IS
    'Self-FK на tradein_users(id). NULL для admin/manager (top-level, CHECK ниже) или для '
    'employee без назначенной организации.';

CREATE TABLE IF NOT EXISTS tradein_sessions (
    token         text PRIMARY KEY,
    user_id       bigint NOT NULL REFERENCES tradein_users(id) ON DELETE CASCADE,
    created_at    timestamptz NOT NULL DEFAULT now(),
    expires_at    timestamptz NOT NULL,
    last_seen_at  timestamptz NOT NULL DEFAULT now(),
    ip_address    inet NULL,
    user_agent    text NULL
);

COMMENT ON TABLE tradein_sessions IS
    'Trade-in DB-backed auth — активные сессии (issue #2551, эпик #2549). '
    'ON DELETE CASCADE от tradein_users: удалённый пользователь теряет все сессии.';

CREATE INDEX IF NOT EXISTS tradein_sessions_expires_at_idx
    ON tradein_sessions (expires_at);

CREATE INDEX IF NOT EXISTS tradein_sessions_user_id_idx
    ON tradein_sessions (user_id);

CREATE INDEX IF NOT EXISTS tradein_users_manager_id_idx
    ON tradein_users (manager_id)
    WHERE manager_id IS NOT NULL;

COMMIT;
