-- Phishing Triage — Postgres schema (v3.0).
--
-- Schema-only migration (Phase 15) — lays down the full durable schema every
-- v3.0 phase reads and writes, before any service writes to it.
--
--   • Campaign grouping (dedupe multiple reports of the same lure)  -> campaigns
--   • One row per reported/triaged ticket (Phase 15 populates this) -> reports
--   • Parsed email content per report                               -> messages (stub, Phase 16)
--   • Extracted URLs/senders/hashes per message                     -> indicators (stub, Phase 16)
--   • Campaign verdict + recommended actions                        -> classifications (stub, Phase 19)
--   • Proposed/approved remediation actions per campaign             -> remediation_actions (stub, Phase 20)
--   • Audit trail of triage/remediation events                       -> audit_events (stub, Phase 20)
--
-- Phase 15 only populates `reports` (via the Plan 02 phishing-detector
-- service). `campaigns`, `messages`, `indicators`, `classifications`,
-- `remediation_actions`, and `audit_events` are laid down now as stubs so
-- Phases 16-21 have their schema ready and never need a second foundation
-- migration. Postgres 16 has built-in gen_random_uuid() — no extension needed.

-- ---------------------------------------------------------------------------
-- 1. campaigns — groups multiple reports of the same phishing lure
-- ---------------------------------------------------------------------------
-- Populated starting Phase 18 (campaign grouping). reports.campaign_id is
-- nullable until then.

CREATE TABLE IF NOT EXISTS campaigns (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    campaign_key    TEXT,
    group_method    TEXT,
    first_seen_at   TIMESTAMPTZ,
    last_seen_at    TIMESTAMPTZ,
    report_count    INTEGER NOT NULL DEFAULT 0,
    status          TEXT NOT NULL DEFAULT 'open',
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_campaigns_campaign_key ON campaigns(campaign_key);

-- ---------------------------------------------------------------------------
-- 2. reports — one row per reported/triaged ticket (the only table Phase 15
--    populates; fully designed here)
-- ---------------------------------------------------------------------------
-- ticket_id is a hard FK to tickets(id) — every report originates from an
-- Autotask ticket already synced into Postgres. content_hash is the D-04
-- idempotency key (hash over title+description) the Plan 02 detector uses to
-- avoid re-processing an unchanged ticket. matched_patterns captures the
-- DETECT-01 pattern strings that matched. evidence captures the EVID-01
-- notes/time_entries/attachments snapshot at detection time. campaign_id is
-- nullable — Phase 18's campaign-grouping logic links it later.
-- uq_reports_ticket_id lets the Plan 02 detector upsert with
-- ON CONFLICT (ticket_id) — one report per ticket.

CREATE TABLE IF NOT EXISTS reports (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    ticket_id               BIGINT NOT NULL REFERENCES tickets(id),
    ticket_number           VARCHAR(100),
    company_id              BIGINT,
    company_name            VARCHAR(255),
    requester_contact_id     BIGINT,
    created_by_contact_id    BIGINT,
    title                   VARCHAR(255),
    description             TEXT,
    matched_patterns        JSONB NOT NULL DEFAULT '[]'::jsonb,
    content_hash            TEXT NOT NULL,
    evidence                JSONB NOT NULL DEFAULT '{}'::jsonb,
    campaign_id             UUID REFERENCES campaigns(id),
    created_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uq_reports_ticket_id UNIQUE (ticket_id)
);

CREATE INDEX IF NOT EXISTS idx_reports_ticket_id ON reports(ticket_id);
CREATE INDEX IF NOT EXISTS idx_reports_content_hash ON reports(content_hash);
CREATE INDEX IF NOT EXISTS idx_reports_campaign_id ON reports(campaign_id);

-- ---------------------------------------------------------------------------
-- 3. messages — parsed email content per report (stub for Phase 16)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS messages (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    report_id       UUID REFERENCES reports(id),
    message_id      TEXT,
    headers         JSONB,
    urls            JSONB,
    attachments     JSONB,
    body_preview    TEXT,
    raw_ref         TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_messages_report_id ON messages(report_id);
CREATE INDEX IF NOT EXISTS idx_messages_message_id ON messages(message_id);

-- ---------------------------------------------------------------------------
-- 4. indicators — extracted URLs/senders/hashes per message (stub for Phase 16)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS indicators (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    message_id          UUID REFERENCES messages(id),
    indicator_type      TEXT NOT NULL,
    value               TEXT NOT NULL,
    created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_indicators_message_id ON indicators(message_id);

-- ---------------------------------------------------------------------------
-- 5. classifications — campaign verdict + recommended actions (stub for Phase 19)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS classifications (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    campaign_id             UUID REFERENCES campaigns(id),
    verdict                 TEXT,
    confidence               NUMERIC,
    summary                 TEXT,
    reasons                 JSONB,
    recommended_actions     JSONB,
    requires_approval       BOOLEAN NOT NULL DEFAULT false,
    created_at              TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_classifications_campaign_id ON classifications(campaign_id);

-- ---------------------------------------------------------------------------
-- 6. remediation_actions — proposed/approved remediation per campaign (stub
--    for Phase 20)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS remediation_actions (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    campaign_id     UUID REFERENCES campaigns(id),
    action_type     TEXT,
    status          TEXT NOT NULL DEFAULT 'proposed',
    params          JSONB,
    approved_by     TEXT,
    approved_at     TIMESTAMPTZ,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_remediation_actions_campaign_id ON remediation_actions(campaign_id);

-- ---------------------------------------------------------------------------
-- 7. audit_events — audit trail of triage/remediation events (stub for Phase 20)
-- ---------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS audit_events (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    campaign_id     UUID,
    actor           TEXT,
    event_type      TEXT NOT NULL,
    payload         JSONB,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
