-- Optional semantic fallback for evidence that does not match an authored source
-- fingerprint. Rules clone with boards; evaluations and their flag provenance
-- belong to one mutable level/document.

CREATE TABLE osint.evidence_semantic_rules (
  id UUID PRIMARY KEY,
  board_id UUID NOT NULL REFERENCES osint.boards(id) ON DELETE CASCADE,
  origin_rule_id UUID REFERENCES osint.evidence_semantic_rules(id) ON DELETE SET NULL,
  goal_id UUID NOT NULL REFERENCES osint.level_goals(id) ON DELETE CASCADE,
  name TEXT NOT NULL CHECK (char_length(btrim(name)) BETWEEN 1 AND 160),
  target_subject TEXT NOT NULL CHECK (char_length(btrim(target_subject)) BETWEEN 1 AND 300),
  related_subject TEXT CHECK (related_subject IS NULL OR char_length(btrim(related_subject)) BETWEEN 1 AND 300),
  assertion_text TEXT NOT NULL CHECK (char_length(btrim(assertion_text)) BETWEEN 1 AND 2000),
  success_flag_key TEXT NOT NULL CHECK (success_flag_key ~ '^[a-z][a-z0-9_.-]{0,63}$'),
  related_flag_key TEXT CHECK (related_flag_key IS NULL OR related_flag_key ~ '^[a-z][a-z0-9_.-]{0,63}$'),
  minimum_confidence NUMERIC(4,3) NOT NULL DEFAULT 0.850 CHECK (minimum_confidence BETWEEN 0.500 AND 1),
  evaluator_version TEXT NOT NULL DEFAULT 'evidence_claim_v1' CHECK (char_length(btrim(evaluator_version)) BETWEEN 1 AND 100),
  enabled BOOLEAN NOT NULL DEFAULT TRUE,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE (board_id,id),
  UNIQUE (board_id,name),
  FOREIGN KEY (board_id,goal_id) REFERENCES osint.level_goals(board_id,id) ON DELETE CASCADE
);
CREATE INDEX evidence_semantic_rules_board_enabled_idx
  ON osint.evidence_semantic_rules (board_id,created_at,id) WHERE enabled;

CREATE TABLE osint.evidence_semantic_evaluations (
  id UUID PRIMARY KEY,
  level_id UUID NOT NULL,
  board_id UUID NOT NULL,
  document_exhibit_id UUID NOT NULL REFERENCES osint.document_exhibits(exhibit_id) ON DELETE CASCADE,
  extraction_id UUID NOT NULL REFERENCES osint.asset_text_extractions(id) ON DELETE CASCADE,
  rule_id UUID NOT NULL,
  evaluator_version TEXT NOT NULL,
  provider TEXT NOT NULL,
  model TEXT NOT NULL,
  status TEXT NOT NULL CHECK (status IN ('pending','succeeded','failed')),
  subject TEXT CHECK (subject IS NULL OR subject IN ('target','related','ambiguous','neither')),
  supports_claim BOOLEAN,
  evidence_excerpt TEXT NOT NULL DEFAULT '',
  confidence NUMERIC(4,3) CHECK (confidence IS NULL OR confidence BETWEEN 0 AND 1),
  failure_code TEXT,
  attempt_count SMALLINT NOT NULL DEFAULT 1 CHECK (attempt_count > 0),
  evaluated_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  FOREIGN KEY (level_id,board_id) REFERENCES osint.levels(id,board_id) ON DELETE CASCADE,
  FOREIGN KEY (board_id,document_exhibit_id) REFERENCES osint.exhibits(board_id,id) ON DELETE CASCADE,
  FOREIGN KEY (board_id,rule_id) REFERENCES osint.evidence_semantic_rules(board_id,id) ON DELETE CASCADE,
  UNIQUE (level_id,document_exhibit_id,rule_id,evaluator_version),
  CHECK (status <> 'succeeded' OR (subject IS NOT NULL AND supports_claim IS NOT NULL AND confidence IS NOT NULL)),
  CHECK (status <> 'failed' OR failure_code IS NOT NULL)
);
CREATE INDEX evidence_semantic_evaluations_level_idx
  ON osint.evidence_semantic_evaluations (level_id,updated_at DESC);

ALTER TABLE osint.level_flags
  ADD COLUMN awarded_by_semantic_evaluation_id UUID REFERENCES osint.evidence_semantic_evaluations(id) ON DELETE SET NULL,
  ADD CONSTRAINT level_flags_single_evidence_provenance CHECK (
    num_nonnulls(awarded_by_evidence_match_id,awarded_by_semantic_evaluation_id) <= 1
  );

COMMENT ON TABLE osint.evidence_semantic_rules IS 'Clonable evidence-only claim classifiers used after deterministic source matching misses.';
COMMENT ON TABLE osint.evidence_semantic_evaluations IS 'Idempotent, auditable semantic verdicts for one level document and authored rule.';
