-- Praxis v0.4 operator-tier Postgres schema (reference).
-- Applied in order by db/pg_migrate.py via db/pg_migrations/*.sql.
-- The canonical migration is 0001_operator_tier.sql; this file is the
-- human-readable reference (kept in sync). Uses gen_random_uuid() which
-- is in PG16 core (no extension needed — R-MT-05 verified).
--
-- Tables:
--   operators            — operator accounts (argon2id password hash)
--   issued_credentials   — VC issuance log (learner_ref is opaque, no FK)
--   mastery_gate_events  — mastery gate audit log (REQ-NFR-MAST-02)
--   cohort_aggregates    — k-anonymized cohort metrics (plain table, D-050)
--   issuer_keys          — Ed25519 issuer key lifecycle (active/superseded)
--
-- No cross-DB FKs (D-031). learner_ref is an opaque string in Postgres.

CREATE TABLE IF NOT EXISTS operators (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username      TEXT UNIQUE NOT NULL,
    password_hash TEXT NOT NULL,
    display_name  TEXT,
    role          TEXT NOT NULL DEFAULT 'operator',
    is_active     BOOLEAN NOT NULL DEFAULT TRUE,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    last_login_at TIMESTAMPTZ
);

CREATE TABLE IF NOT EXISTS issued_credentials (
    id            UUID PRIMARY KEY,
    operator_id   UUID REFERENCES operators(id),
    learner_ref   TEXT NOT NULL,
    vc_type       TEXT,
    payload_jsonb JSONB NOT NULL,
    signature_b64 TEXT NOT NULL,
    status        TEXT NOT NULL DEFAULT 'active',
    issued_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    revoked_at    TIMESTAMPTZ
);

CREATE TABLE IF NOT EXISTS mastery_gate_events (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    learner_ref         TEXT NOT NULL,
    scenario_id         TEXT,
    path_id             TEXT NOT NULL,
    gate_outcome        TEXT,
    rubric_scores_jsonb JSONB,
    recorded_at         TIMESTAMPTZ NOT NULL DEFAULT now(),
    source              TEXT NOT NULL DEFAULT 'sync'
);

CREATE TABLE IF NOT EXISTS cohort_aggregates (
    path           TEXT NOT NULL,
    metric         TEXT NOT NULL,
    window_start   DATE NOT NULL,
    window_end     DATE NOT NULL,
    value          NUMERIC,
    cell_count     INTEGER NOT NULL DEFAULT 0,
    cell_suppressed BOOLEAN NOT NULL DEFAULT FALSE,
    updated_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (path, metric, window_start)
);
-- Plain table, NOT partitioned (D-050..D-053; add partitioning post-pilot).
CREATE INDEX IF NOT EXISTS cohort_aggregates_path_window_idx
    ON cohort_aggregates (path, window_start);

CREATE TABLE IF NOT EXISTS issuer_keys (
    id               TEXT PRIMARY KEY,
    public_key       TEXT NOT NULL,
    private_key_enc  BYTEA,
    status           TEXT NOT NULL DEFAULT 'active',
    created_at       TIMESTAMPTZ NOT NULL DEFAULT now()
);