-- Data-defined evidence recognition for player-uploaded sources. Text extraction
-- belongs to the immutable asset; matching rules belong to a clonable board;
-- evaluations and awarded flags belong to the mutable level instance.

CREATE TABLE osint.asset_text_extractions (
  id UUID PRIMARY KEY,
  asset_id UUID NOT NULL REFERENCES osint.assets(id) ON DELETE CASCADE,
  extractor TEXT NOT NULL,
  extractor_version TEXT NOT NULL,
  language TEXT NOT NULL,
  status TEXT NOT NULL CHECK (status IN ('succeeded','unsupported','failed')),
  extracted_text TEXT NOT NULL DEFAULT '',
  error_message TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE (asset_id,extractor,extractor_version,language)
);
CREATE INDEX asset_text_extractions_asset_idx ON osint.asset_text_extractions (asset_id,created_at DESC);

CREATE TABLE osint.evidence_match_rules (
  id UUID PRIMARY KEY,
  board_id UUID NOT NULL REFERENCES osint.boards(id) ON DELETE CASCADE,
  origin_rule_id UUID REFERENCES osint.evidence_match_rules(id) ON DELETE SET NULL,
  name TEXT NOT NULL,
  flag_key TEXT NOT NULL CHECK (flag_key ~ '^[a-z][a-z0-9_.-]{0,63}$'),
  matcher_version TEXT NOT NULL DEFAULT 'char_trigram_v1' CHECK (matcher_version = 'char_trigram_v1'),
  minimum_anchor_matches SMALLINT NOT NULL DEFAULT 1 CHECK (minimum_anchor_matches > 0),
  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)
);
CREATE INDEX evidence_match_rules_board_flag_idx ON osint.evidence_match_rules (board_id,flag_key) WHERE enabled;

CREATE TABLE osint.evidence_match_anchors (
  id UUID PRIMARY KEY,
  rule_id UUID NOT NULL REFERENCES osint.evidence_match_rules(id) ON DELETE CASCADE,
  phrase_text TEXT NOT NULL CHECK (char_length(btrim(phrase_text)) >= 12),
  minimum_similarity NUMERIC(4,3) NOT NULL DEFAULT 0.720 CHECK (minimum_similarity >= 0.500 AND minimum_similarity <= 1),
  sort_order SMALLINT NOT NULL DEFAULT 0 CHECK (sort_order >= 0),
  UNIQUE (rule_id,sort_order)
);

CREATE TABLE osint.evidence_match_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,
  matched BOOLEAN NOT NULL,
  matched_anchor_count SMALLINT NOT NULL CHECK (matched_anchor_count >= 0),
  score NUMERIC(4,3) NOT NULL CHECK (score >= 0 AND score <= 1),
  evaluated_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_match_rules(board_id,id) ON DELETE CASCADE,
  UNIQUE (level_id,document_exhibit_id,rule_id)
);
CREATE INDEX evidence_match_evaluations_level_idx ON osint.evidence_match_evaluations (level_id,evaluated_at DESC);

CREATE TABLE osint.evidence_match_anchor_evaluations (
  evaluation_id UUID NOT NULL REFERENCES osint.evidence_match_evaluations(id) ON DELETE CASCADE,
  anchor_id UUID NOT NULL REFERENCES osint.evidence_match_anchors(id) ON DELETE CASCADE,
  similarity NUMERIC(4,3) NOT NULL CHECK (similarity >= 0 AND similarity <= 1),
  matched BOOLEAN NOT NULL,
  matched_text TEXT NOT NULL DEFAULT '',
  PRIMARY KEY (evaluation_id,anchor_id)
);

ALTER TABLE osint.level_flags
  ADD COLUMN awarded_by_evidence_match_id UUID REFERENCES osint.evidence_match_evaluations(id) ON DELETE SET NULL;

COMMENT ON TABLE osint.asset_text_extractions IS 'Cached OCR or direct-text extraction for one immutable uploaded asset.';
COMMENT ON TABLE osint.evidence_match_rules IS 'Clonable level-authored rule that awards a flag when enough distinctive text anchors match an uploaded source.';
COMMENT ON TABLE osint.evidence_match_anchors IS 'Alternative or cumulative reference passages used by one evidence match rule.';
COMMENT ON TABLE osint.evidence_match_evaluations IS 'Auditable result of applying one board rule to one player-uploaded document.';
COMMENT ON TABLE osint.evidence_match_anchor_evaluations IS 'Per-anchor fuzzy score and best normalized text window for an evidence evaluation.';
