-- 144_audit_log.sql
-- #962 (EPIC18, ТЗ §19): аудит-лог чувствительных действий аналитика/пользователей.
--
-- Пишется best-effort FastAPI-middleware (app/core/audit_middleware.py) ПОСЛЕ
-- ответа на запрос для ключевых действий:
--   POST /api/v1/parcels/{cad}/analyze          → action='analyze'
--   GET  /api/v1/parcels/{cad}/forecast         → action='forecast'
--   GET  /api/v1/parcels/{cad}/forecast/export  → action='export'
-- Прочие matched-нестандартные пути → action='other'. /health, static,
-- non-matched пути НЕ аудируются.
--
-- Best-effort: сбой записи в audit_log НИКОГДА не ломает основной запрос
-- (middleware оборачивает write в try/except).
--
-- Deploy: auto-applied deploy.yml через _schema_migrations (ровно один раз NN=144).
-- Idempotent: CREATE TABLE/INDEX IF NOT EXISTS + guarded DO-блок для CHECK.

BEGIN;

-- ── 1. Таблица audit_log ──────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS audit_log (
    id          BIGSERIAL PRIMARY KEY,
    username    TEXT        NOT NULL,            -- из X-Authenticated-User
    role        TEXT,                            -- get_role(username) на момент запроса
    action      TEXT        NOT NULL,            -- analyze | forecast | export | other
    method      TEXT        NOT NULL,            -- HTTP-метод (GET/POST/...)
    path        TEXT        NOT NULL,            -- request.url.path
    cad_num     TEXT,                            -- кадастровый номер из пути (NULL если нет)
    status_code INTEGER,                         -- HTTP-статус ответа
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    detail      JSONB                            -- произвольный контекст (резерв)
);

COMMENT ON TABLE audit_log IS
    'Аудит чувствительных действий (#962, ТЗ §19). Пишется best-effort '
    'middleware app/core/audit_middleware.py ПОСЛЕ ответа на запрос. '
    'Сбой записи не влияет на основной запрос.';
COMMENT ON COLUMN audit_log.action IS
    'analyze | forecast | export | other — выводится из request path в middleware.';
COMMENT ON COLUMN audit_log.cad_num IS
    'Кадастровый номер из /parcels/{cad}/... (напр. 66:41:0204016:10), NULL если нет.';
COMMENT ON COLUMN audit_log.detail IS
    'Резервное JSONB-поле под доп. контекст (пока не заполняется).';

-- ── 2. CHECK на допустимые action (guarded — idempotent при ре-apply) ────────
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM pg_constraint WHERE conname = 'ck_audit_log_action'
    ) THEN
        ALTER TABLE audit_log
            ADD CONSTRAINT ck_audit_log_action
            CHECK (action IN ('analyze', 'forecast', 'export', 'other'));
    END IF;
END
$$;

-- ── 3. Индексы: lookup по юзеру во времени + по кадастру ──────────────────────
CREATE INDEX IF NOT EXISTS idx_audit_log_username_created
    ON audit_log (username, created_at);

CREATE INDEX IF NOT EXISTS idx_audit_log_cad_num
    ON audit_log (cad_num);

COMMIT;
