-- WP-0009-T03: Control Plane backend — audit log and proposed-entry review.
-- Depends on migrations/0001_registries.sql, 0002_ledger.sql, and
-- 0005_licensor_credentials.sql (credential_label/rights/licensor_identities).
--
-- These tables belong to the Control Plane, a client layer on top of the
-- already-finished hosted Trust Service (registry.py/ledger.py) — not new
-- Trust Service surface itself. Per
-- specs/TargetRevenueControlPlaneConcept.md §5: the audit log here is
-- deliberately separate from the Trust Service's own signed records
-- (phase_manifests, ledger_entries, attestations, breach_records), which
-- only ever attest "the tenant did this," never the individual human.

BEGIN;

-- Append-only audit trail: which credential (hence which human) took
-- which Control Plane action, and — where applicable — which Trust
-- Service record resulted. `trf_app` has no UPDATE/DELETE grant: an audit
-- log that could be edited after the fact isn't an audit log.
CREATE TABLE IF NOT EXISTS control_plane_audit_log (
    id                      bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    actor_token             text NOT NULL REFERENCES licensors(token),
    actor_licensor_id       text NOT NULL,
    actor_credential_label  text,
    action                  text NOT NULL,
    phase_id                text,
    trust_service_record_id text,
    detail                  jsonb,
    created_at              timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS control_plane_audit_log_phase_idx
    ON control_plane_audit_log (phase_id);

GRANT SELECT, INSERT ON control_plane_audit_log TO trf_app;

-- Contributor-tier proposed entries, per
-- specs/TargetRevenueControlPlaneConcept.md §2's rights table: a
-- Contributor may submit a proposed Development Credit entry but not
-- append it directly; an Operator/Admin reviews and either approves
-- (which actually appends it to the real Ledger under the reviewer's own
-- credential) or rejects it. `trf_app` again has no UPDATE/DELETE grant —
-- review decisions are recorded via a SECURITY DEFINER function, the same
-- governance-action pattern as `set_extension_status`/`revoke_credential`.
CREATE TABLE IF NOT EXISTS control_plane_proposed_entries (
    id                bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    phase_id          text NOT NULL,
    entry             jsonb NOT NULL,
    proposed_by_token text NOT NULL REFERENCES licensors(token),
    status            text NOT NULL DEFAULT 'pending'
                        CHECK (status IN ('pending', 'approved', 'rejected')),
    reviewed_by_token text REFERENCES licensors(token),
    reviewed_at       timestamptz,
    review_note       text,
    appended_entry_id text,
    created_at        timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS control_plane_proposed_entries_phase_idx
    ON control_plane_proposed_entries (phase_id, status);

GRANT SELECT, INSERT ON control_plane_proposed_entries TO trf_app;

CREATE OR REPLACE FUNCTION review_proposed_entry(
    p_id bigint,
    p_status text,
    p_reviewed_by_token text,
    p_review_note text,
    p_appended_entry_id text
) RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
    IF p_status NOT IN ('approved', 'rejected') THEN
        RAISE EXCEPTION 'review_proposed_entry only permits approved/rejected, got %', p_status;
    END IF;
    UPDATE control_plane_proposed_entries
       SET status = p_status,
           reviewed_by_token = p_reviewed_by_token,
           reviewed_at = now(),
           review_note = p_review_note,
           appended_entry_id = p_appended_entry_id
     WHERE id = p_id AND status = 'pending';
    IF NOT FOUND THEN
        RAISE EXCEPTION 'no pending proposal with id %', p_id;
    END IF;
END;
$$;

GRANT EXECUTE ON FUNCTION review_proposed_entry(bigint, text, text, text, text) TO trf_app;

COMMIT;
