-- sql/138-transceiver-speed-drift-guard.sql
-- Closes a gap in the sql/125+126 "no physically-impossible compatibility rows"
-- invariant: compat_speed_guard() (126) only fires on INSERT/UPDATE of the
-- `compatibility` table itself. It never re-validates existing 'compatible'
-- spec_match rows when a TRANSCEIVER's speed_gbps changes later (e.g. the daily
-- enrich-modulation-fec.sh self-heal, sql/135, recovering speed from part-number
-- tokens for previously NULL/junk rows). Found live 2026-07-12: 189 compat rows
-- had gone physically-impossible because their transceiver's speed_gbps was
-- updated (all sampled rows dated today, i.e. by the 03:30 cron) after the
-- compat row was already written as 'compatible'.
--
-- Fix mirrors 126 exactly, just triggered from the other table: AFTER UPDATE OF
-- speed_gbps on transceivers, re-validate every 'compatible' spec_match
-- compatibility row referencing that transceiver against the same evidence-based
-- ceiling (v_switch_effective_max) used by v_compat_speed_errors. Curated rows
-- (vendor_matrix/vendor_compat/tested/datasheet) are never touched. Idempotent,
-- safe to re-run.

-- Backup of affected rows before remediation (audit trail, matches
-- tip_speed_gbps_rollback precedent from sql/136).
create table if not exists compat_speed_drift_backup_20260712 as
with em as materialized (
  select switch_id, effective_max_gbps from v_switch_effective_max)
select c.*, e.effective_max_gbps, t.speed_gbps as tx_speed_gbps, now() as backed_up_at
from compatibility c
join em e on e.switch_id = c.switch_id
join transceivers t on t.id = c.transceiver_id
where c.status = 'compatible'
  and c.verification_method = 'spec_match'
  and e.effective_max_gbps > 0
  and t.speed_gbps is not null
  and t.speed_gbps > e.effective_max_gbps;

-- One-time remediation: flip the rows found live today (same logic as 125's
-- Remediation B, using the full evidence-based ceiling incl. curated bump).
with em as materialized (
  select switch_id, effective_max_gbps from v_switch_effective_max)
update compatibility c
   set status = 'incompatible',
       notes  = case when c.notes is null or c.notes = ''
                     then 'auto(138): transceiver speed exceeds switch max port speed (post-hoc drift)'
                     else c.notes || ' | auto(138): transceiver speed exceeds switch max port speed (post-hoc drift)' end
  from em e, transceivers t
 where e.switch_id = c.switch_id
   and t.id = c.transceiver_id
   and c.status = 'compatible'
   and c.verification_method = 'spec_match'
   and e.effective_max_gbps > 0
   and t.speed_gbps is not null
   and t.speed_gbps > e.effective_max_gbps;

-- Durable fix: re-validate on transceiver speed change.
create or replace function compat_speed_guard_on_transceiver_change() returns trigger
language plpgsql as $$
begin
  if NEW.speed_gbps is distinct from OLD.speed_gbps and NEW.speed_gbps is not null then
    update compatibility c
       set status = 'incompatible',
           notes  = case when c.notes is null or c.notes = ''
                         then 'auto(138): transceiver speed exceeds switch max port speed (post-hoc drift)'
                         else c.notes || ' | auto(138): transceiver speed exceeds switch max port speed (post-hoc drift)' end
      from v_switch_effective_max e
     where e.switch_id = c.switch_id
       and c.transceiver_id = NEW.id
       and c.status = 'compatible'
       and c.verification_method = 'spec_match'
       and e.effective_max_gbps > 0
       and NEW.speed_gbps > e.effective_max_gbps;
  end if;
  return NEW;
end $$;

comment on function compat_speed_guard_on_transceiver_change() is
  'AFTER UPDATE OF speed_gbps on transceivers: re-validates existing compatible spec_match rows for this transceiver against the evidence-based switch ceiling, closing the drift gap left by compat_speed_guard() (126), which only fires on compatibility writes. Never touches curated rows.';

drop trigger if exists trg_compat_speed_guard_on_transceiver_change on transceivers;
create trigger trg_compat_speed_guard_on_transceiver_change
  after update of speed_gbps on transceivers
  for each row execute function compat_speed_guard_on_transceiver_change();

insert into _migrations (filename, applied_at)
select '138-transceiver-speed-drift-guard.sql', now()
where not exists (select 1 from _migrations where filename = '138-transceiver-speed-drift-guard.sql');
