-- v14.0 governed feedback-cycle delta.
-- This migration formalizes evidence-to-hypothesis-to-independent-validation history.
-- It does not enable automatic model activation, client publication, automatic order submission or KIS submission.

create table if not exists evaluation.model_feedback_cycle (
    cycle_id uuid primary key,
    scope_key text not null,
    base_model_version text not null,
    state text not null check (state in (
        'PLANNED','PREDICTION_FROZEN','OUTCOMES_MATURING','EVALUATED','IMPROVEMENT_PROPOSED',
        'CHALLENGER_PLANNED','INDEPENDENTLY_VALIDATED','PROMOTION_REVIEW_PENDING','BUSINESS_HOLD','CLOSED')),
    revision integer not null check (revision > 0),
    started_at timestamptz not null,
    closed_at timestamptz null,
    last_evidence_hash text not null,
    updated_at timestamptz not null,
    check ((state = 'CLOSED') = (closed_at is not null))
);

create index if not exists ix_model_feedback_cycle_scope_state
    on evaluation.model_feedback_cycle (scope_key, state, updated_at desc);

create table if not exists evaluation.model_feedback_transition (
    transition_id uuid primary key,
    cycle_id uuid not null references evaluation.model_feedback_cycle(cycle_id),
    sequence_no integer not null check (sequence_no > 0),
    from_state text not null,
    to_state text not null,
    reason_code text not null,
    evidence_hash text not null,
    actor_id text not null,
    actor_type text not null check (actor_type in ('SYSTEM','OPERATOR','INDEPENDENT_VALIDATOR','INVESTMENT_COMMITTEE')),
    correlation_id text not null,
    occurred_at timestamptz not null,
    content_hash text not null unique,
    unique (cycle_id, sequence_no)
);

create table if not exists governance.model_hypothesis_evidence (
    hypothesis_evidence_id uuid primary key,
    hypothesis_id uuid not null references governance.model_improvement_hypothesis(hypothesis_id),
    side text not null check (side in ('SUPPORTING','COUNTER')),
    classification text not null check (classification in ('SOURCE','ASSUMPTION','UNKNOWN','DECISION_REQUIRED')),
    reference_code text not null,
    statement text not null,
    content_hash text not null,
    created_at timestamptz not null default now(),
    unique (hypothesis_id, side, reference_code, content_hash)
);

create table if not exists governance.model_activation_decision (
    activation_decision_id uuid primary key,
    scope_key text not null,
    candidate_model_version text not null,
    promotion_review_id uuid not null references governance.model_promotion_review(review_id),
    decision text not null check (decision in ('KEEP_CURRENT_CHAMPION','ACTIVATE_FOR_SHADOW','ACTIVATE_FOR_PILOT','REJECT','EXPIRE')),
    effective_at timestamptz null,
    rollback_model_version text not null,
    maker_id text not null,
    checker_id text not null,
    actor_type text not null check (actor_type = 'HUMAN_CHANGE_APPROVAL'),
    change_ticket text not null,
    evidence_bundle_hash text not null,
    content_hash text not null unique,
    decided_at timestamptz not null,
    check (maker_id <> checker_id),
    check (decision not in ('ACTIVATE_FOR_SHADOW','ACTIVATE_FOR_PILOT') or effective_at is not null)
);

-- Immutable evidence. The cycle aggregate itself remains mutable only through optimistic application logic;
-- every transition is captured here and cannot be edited or deleted.
drop trigger if exists model_feedback_transition_append_only on evaluation.model_feedback_transition;
create trigger model_feedback_transition_append_only
before update or delete on evaluation.model_feedback_transition
for each row execute function building_blocks.prevent_append_only_change();

drop trigger if exists model_hypothesis_evidence_append_only on governance.model_hypothesis_evidence;
create trigger model_hypothesis_evidence_append_only
before update or delete on governance.model_hypothesis_evidence
for each row execute function building_blocks.prevent_append_only_change();

drop trigger if exists model_activation_decision_append_only on governance.model_activation_decision;
create trigger model_activation_decision_append_only
before update or delete on governance.model_activation_decision
for each row execute function building_blocks.prevent_append_only_change();

comment on table governance.model_activation_decision is
'Human-only activation decision. Scheduler, Hangfire worker and proposal jobs must not insert into this table.';

-- Integrity audit is disabled until cycle aging, ownership and alert contracts are approved.
insert into evaluation.model_operation_schedule
(schedule_id, operation_code, operation_name, scope_key, cadence, automation_mode, queue_name,
 schedule_version, enabled, next_due_at, max_lag, primary_owner, secondary_owner,
 calendar_id, due_policy, dependency_json, catch_up_policy, max_catch_up)
values
('e4100000-0000-4000-8000-000000000039','J39','FeedbackCycleIntegrityAudit','GLOBAL','DAILY','EVALUATION_ONLY','q-control',1,false,now()+interval '1 day',interval '1 day','Compliance/QA','Quant/SRE','GLOBAL_UTC','FIXED_CADENCE','["FEEDBACK_CYCLE_CONTRACT_APPROVED","AGING_POLICY_APPROVED"]','LATEST_ONLY',1)
on conflict (operation_code, scope_key, schedule_version) do nothing;
