-- Document Reconciliation v2 (additive; no data backfill is run here).
CREATE EXTENSION IF NOT EXISTS pgcrypto;

CREATE TABLE IF NOT EXISTS opportunity_document_links (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    opportunity_id UUID NOT NULL REFERENCES opportunities(id) ON DELETE RESTRICT,
    document_id UUID NOT NULL REFERENCES commercial_documents(id) ON DELETE RESTRICT,
    document_kind TEXT NOT NULL,
    relationship TEXT NOT NULL CHECK (relationship IN ('PRIMARY','SECONDARY','HISTORICAL','IGNORED','REMOVED','REASSIGNED','REVIEW_REQUIRED')),
    is_manual BOOLEAN NOT NULL DEFAULT FALSE,
    decision_reason TEXT,
    decided_by TEXT,
    decided_at TIMESTAMPTZ,
    source TEXT NOT NULL,
    origin_opportunity_id UUID REFERENCES opportunities(id) ON DELETE RESTRICT,
    destination_opportunity_id UUID REFERENCES opportunities(id) ON DELETE RESTRICT,
    correlation_id TEXT,
    request_id TEXT,
    restored_from_link_id UUID REFERENCES opportunity_document_links(id) ON DELETE SET NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    ended_at TIMESTAMPTZ,
    metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
    version INTEGER NOT NULL DEFAULT 1 CHECK (version > 0),
    CONSTRAINT ck_document_link_reassigned CHECK (
      relationship <> 'REASSIGNED' OR (
        origin_opportunity_id IS NOT NULL AND destination_opportunity_id IS NOT NULL
        AND origin_opportunity_id <> destination_opportunity_id
      )
    )
);

CREATE UNIQUE INDEX IF NOT EXISTS ux_document_link_current
ON opportunity_document_links(opportunity_id, document_id) WHERE ended_at IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS ux_document_link_primary_kind
ON opportunity_document_links(opportunity_id, document_kind) WHERE ended_at IS NULL AND relationship = 'PRIMARY';
CREATE INDEX IF NOT EXISTS idx_document_links_opportunity ON opportunity_document_links(opportunity_id, ended_at, relationship);
CREATE INDEX IF NOT EXISTS idx_document_links_document ON opportunity_document_links(document_id, ended_at);
CREATE INDEX IF NOT EXISTS idx_document_links_relationship ON opportunity_document_links(relationship, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_document_links_destination ON opportunity_document_links(destination_opportunity_id) WHERE destination_opportunity_id IS NOT NULL;

CREATE TABLE IF NOT EXISTS opportunity_document_link_events (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    link_id UUID REFERENCES opportunity_document_links(id) ON DELETE SET NULL,
    opportunity_id UUID NOT NULL REFERENCES opportunities(id) ON DELETE RESTRICT,
    document_id UUID NOT NULL REFERENCES commercial_documents(id) ON DELETE RESTRICT,
    event_type TEXT NOT NULL,
    actor TEXT NOT NULL,
    reason TEXT,
    old_relationship TEXT,
    new_relationship TEXT,
    old_opportunity_id UUID,
    new_opportunity_id UUID,
    old_jasmin_status TEXT,
    new_jasmin_status TEXT,
    correlation_id TEXT,
    request_id TEXT,
    idempotency_key TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    payload JSONB NOT NULL DEFAULT '{}'::jsonb
);
CREATE UNIQUE INDEX IF NOT EXISTS ux_document_link_events_idempotency
ON opportunity_document_link_events(idempotency_key) WHERE idempotency_key IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_document_link_events_link ON opportunity_document_link_events(link_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_document_link_events_opportunity ON opportunity_document_link_events(opportunity_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_document_link_events_document ON opportunity_document_link_events(document_id, created_at DESC);

-- Command claims make the mutation itself idempotent, not merely its event.
CREATE TABLE IF NOT EXISTS document_reconciliation_commands (
    idempotency_key TEXT PRIMARY KEY,
    fingerprint TEXT NOT NULL,
    command_payload JSONB NOT NULL,
    result_payload JSONB,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    completed_at TIMESTAMPTZ
);

-- Events are an audit ledger: UPDATE and DELETE are rejected at database level.
CREATE OR REPLACE FUNCTION reject_document_link_event_mutation() RETURNS trigger
LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION 'opportunity_document_link_events is append-only'; END $$;
DROP TRIGGER IF EXISTS trg_document_link_events_append_only ON opportunity_document_link_events;
CREATE TRIGGER trg_document_link_events_append_only BEFORE UPDATE OR DELETE ON opportunity_document_link_events
FOR EACH ROW EXECUTE FUNCTION reject_document_link_event_mutation();

ALTER TABLE commercial_document_lines ADD COLUMN IF NOT EXISTS commercial_document_id UUID REFERENCES commercial_documents(id) ON DELETE RESTRICT;
UPDATE commercial_document_lines SET commercial_document_id = document_id WHERE commercial_document_id IS NULL;
DO $$ BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='ck_commercial_document_lines_document_present') THEN
    ALTER TABLE commercial_document_lines ADD CONSTRAINT ck_commercial_document_lines_document_present
      CHECK (commercial_document_id IS NOT NULL) NOT VALID;
  END IF;
END $$;
ALTER TABLE commercial_document_lines VALIDATE CONSTRAINT ck_commercial_document_lines_document_present;
ALTER TABLE commercial_document_lines ALTER COLUMN commercial_document_id SET NOT NULL;
ALTER TABLE commercial_document_lines ADD COLUMN IF NOT EXISTS opportunity_document_link_id UUID REFERENCES opportunity_document_links(id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS idx_document_lines_commercial_document ON commercial_document_lines(commercial_document_id);
CREATE INDEX IF NOT EXISTS idx_document_lines_opportunity_link ON commercial_document_lines(opportunity_document_link_id);

COMMENT ON TABLE opportunity_document_links IS 'Canonical ClientFlow document/opportunity relationship; legacy commercial_documents fields are not authoritative.';
COMMENT ON TABLE opportunity_document_link_events IS 'Append-only reconciliation audit ledger.';
