-- version: 14
-- Relax PRIMARY KEYs on `claims` and `remittances` from single-column (id)
-- to composite (batch_id, id). Enables resubmits (same CLM01 / CLP01 in
-- different batches) and makes the pre-flight dedup workflow exercisable.
--
-- Strategy (mirrors 0013): table recreation with PRAGMA
-- defer_foreign_keys. We must recreate every table that has an FK pointing
-- at `claims(id)` or `remittances(id)` so that FK constraints can be
-- updated to point at the new composite PK.
--
-- FKs that need updating (verified via pragma_foreign_key_list on the
-- pre-migration schema):
--   - remittances.claim_id              -> claims(id)              becomes -> (batch_id, id)
--   - claims.matched_remittance_id      -> remittances(id)         becomes -> (batch_id, id)
--   - matches.claim_id                  -> claims(id) ON DELETE CASCADE  becomes -> (batch_id, id)
--   - matches.remittance_id             -> remittances(id) ON DELETE CASCADE  becomes -> (batch_id, id)
--   - cas_adjustments.remittance_id     -> remittances(id) ON DELETE CASCADE  becomes -> (batch_id, id)
--   - service_line_payments.remittance_id -> remittances(id) ON DELETE CASCADE  becomes -> (batch_id, id)
--   - line_reconciliations.claim_id     -> claims(id) ON DELETE CASCADE  becomes -> (batch_id, id)
--
-- The cross-table FKs (remittances.claim_id, claims.matched_remittance_id)
-- can NOT be SQL-enforced with composite PKs because SQLite has no
-- ALTER TABLE ADD CONSTRAINT and DROP/ADD CONSTRAINT for an existing FK.
-- We keep them as plain TEXT columns (no REFERENCES clause) and rely on
-- application-layer invariants (store.manual_match / manual_unmatch,
-- dedup.preflight_*) to keep them consistent. The application code already
-- owns those paths; this is a deliberate trade-off documented in 0001's
-- comments ("amendment deferred to post-SQLite rollout").
--
-- The single-column FKs from claims/remittances DOWN to other tables
-- (matches, cas_adjustments, service_line_payments, line_reconciliations)
-- MUST be updated to composite FKs because those tables are recreated with
-- composite FK columns. We add a `batch_id` (or `remittance_batch_id`)
-- column to each and JOIN against the parent table to populate it during
-- the migration.
--
-- Why PRAGMA defer_foreign_keys (and not foreign_keys=OFF): the migration
-- runner wraps each .sql file in `engine.begin()` (a transaction). Inside a
-- transaction, `PRAGMA defer_foreign_keys = ON` defers FK checks to COMMIT,
-- which is the only safe way to drop a referenced table. `PRAGMA
-- foreign_keys` cannot be changed inside a transaction in SQLite, so the
-- defer_foreign_keys approach is what works inside the runner's transaction.

PRAGMA defer_foreign_keys = ON;

-- ============================================================================
-- Step 1: recreate `remittances` with composite PK (batch_id, id).
-- ============================================================================
-- Column shape: every column from 0001_initial.sql + claim_level_adjustment_amount
-- (added by 0006_line_reconciliation.sql). The `claim_id` column is kept
-- without an SQL-level FK — composite-FK to claims is enforced at the
-- application layer (see plan).
CREATE TABLE remittances_new (
    id TEXT NOT NULL,
    batch_id TEXT NOT NULL REFERENCES batches(id) ON DELETE CASCADE,
    payer_claim_control_number TEXT NOT NULL,
    claim_id TEXT,
    status_code TEXT NOT NULL,
    status_label TEXT,
    total_charge NUMERIC(12, 2) NOT NULL DEFAULT 0,
    total_paid NUMERIC(12, 2) NOT NULL DEFAULT 0,
    patient_responsibility NUMERIC(12, 2),
    adjustment_amount NUMERIC(12, 2) NOT NULL DEFAULT 0,
    claim_level_adjustment_amount NUMERIC(12, 2) NOT NULL DEFAULT 0,
    received_at DATETIME NOT NULL,
    service_date DATE,
    is_reversal INTEGER NOT NULL DEFAULT 0,
    raw_json TEXT,
    PRIMARY KEY (batch_id, id)
);
INSERT INTO remittances_new
    SELECT id, batch_id, payer_claim_control_number, claim_id, status_code,
           status_label, total_charge, total_paid, patient_responsibility,
           adjustment_amount, claim_level_adjustment_amount, received_at,
           service_date, is_reversal, raw_json
    FROM remittances;
DROP TABLE remittances;
ALTER TABLE remittances_new RENAME TO remittances;
CREATE INDEX ix_remittances_claim_id ON remittances(claim_id);
CREATE INDEX ix_remittances_payer_claim_control_number ON remittances(payer_claim_control_number);
CREATE INDEX ix_remittances_status_code ON remittances(status_code);

-- ============================================================================
-- Step 2: recreate `claims` with composite PK (batch_id, id).
-- ============================================================================
-- Column shape: every column from 0013_drop_claims_unique_constraint.sql
-- (which itself consolidated 0001 + 0004 + 0008 + 0010), plus a new
-- `matched_remittance_batch_id` column. The back-reference to remittances
-- becomes a 2-column pointer (matched_remittance_id + matched_remittance_batch_id)
-- but the SQL-level FK is dropped (no ALTER CONSTRAINT in SQLite); see plan.
--
-- Column order MUST match the 0013 column order exactly so that
-- `INSERT INTO claims_new SELECT ... FROM claims` populates the right
-- columns. The trailing NULL fills the new matched_remittance_batch_id.
CREATE TABLE claims_new (
    id TEXT NOT NULL,
    batch_id TEXT NOT NULL REFERENCES batches(id) ON DELETE CASCADE,
    patient_control_number TEXT NOT NULL,
    service_date_from DATE,
    service_date_to DATE,
    charge_amount NUMERIC(12, 2) NOT NULL DEFAULT 0,
    provider_npi TEXT,
    payer_id TEXT,
    state TEXT NOT NULL DEFAULT 'submitted',
    state_before_reversal TEXT,
    -- matched_remittance_id: kept without an SQL-level FK (composite
    -- back-reference requires the application layer; see plan header).
    matched_remittance_id TEXT,
    raw_json TEXT,
    rejection_reason TEXT,
    rejected_at TIMESTAMP,
    resubmit_count INTEGER NOT NULL DEFAULT 0,
    state_changed_at TIMESTAMP,
    payer_rejected_at TEXT,
    payer_rejected_reason TEXT,
    payer_rejected_status_code TEXT,
    payer_rejected_by_277ca_id TEXT,
    payer_rejected_acknowledged_at TEXT,
    payer_rejected_acknowledged_actor TEXT,
    -- matched_remittance_batch_id: NEW. Always NULL on migration because
    -- the pre-0014 schema only stored matched_remittance_id (single column).
    -- App code populates it on manual_match / match_auto going forward.
    matched_remittance_batch_id TEXT,
    PRIMARY KEY (batch_id, id)
);
INSERT INTO claims_new
    SELECT id, batch_id, patient_control_number, service_date_from,
           service_date_to, charge_amount, provider_npi, payer_id, state,
           state_before_reversal, matched_remittance_id, raw_json,
           rejection_reason, rejected_at, resubmit_count, state_changed_at,
           payer_rejected_at, payer_rejected_reason,
           payer_rejected_status_code, payer_rejected_by_277ca_id,
           payer_rejected_acknowledged_at, payer_rejected_acknowledged_actor,
           NULL
    FROM claims;
DROP TABLE claims;
ALTER TABLE claims_new RENAME TO claims;
CREATE INDEX ix_claims_state ON claims(state);
CREATE INDEX ix_claims_patient_control_number ON claims(patient_control_number);
CREATE INDEX ix_claims_service_date_from ON claims(service_date_from);
CREATE INDEX ix_claims_state_changed_at ON claims(state, state_changed_at);
CREATE INDEX idx_claims_payer_rejected_at ON claims(payer_rejected_at);
CREATE INDEX idx_claims_payer_rejected_unack
    ON claims(payer_rejected_at)
    WHERE payer_rejected_acknowledged_at IS NULL;

-- ============================================================================
-- Step 3: recreate `matches`, `cas_adjustments`, `service_line_payments`,
-- `line_reconciliations` so their FKs point at the new composite PKs.
--
-- At this point the OLD `claims` and `remittances` tables are gone (dropped
-- in Steps 1 and 2) — only the recreated (composite-PK) versions exist.
-- The JOIN below reads the NEW parents, which have identical data
-- (same row count, same id values, same batch_id values); we only need
-- `batch_id` from each parent to populate the composite-FK column.
-- ============================================================================

CREATE TABLE matches_new (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    claim_id TEXT NOT NULL,
    batch_id TEXT NOT NULL,
    remittance_id TEXT NOT NULL,
    remittance_batch_id TEXT NOT NULL,
    strategy TEXT NOT NULL,
    matched_at DATETIME NOT NULL,
    prior_claim_state TEXT,
    is_reversal INTEGER NOT NULL DEFAULT 0,
    FOREIGN KEY (batch_id, claim_id) REFERENCES claims(batch_id, id) ON DELETE CASCADE,
    FOREIGN KEY (remittance_batch_id, remittance_id) REFERENCES remittances(batch_id, id) ON DELETE CASCADE
);
INSERT INTO matches_new
    SELECT m.id, m.claim_id, c.batch_id, m.remittance_id, r.batch_id,
           m.strategy, m.matched_at, m.prior_claim_state, m.is_reversal
    FROM matches m
    JOIN claims c ON c.id = m.claim_id
    JOIN remittances r ON r.id = m.remittance_id;
DROP TABLE matches;
ALTER TABLE matches_new RENAME TO matches;
CREATE INDEX ix_matches_claim_id ON matches(claim_id);
CREATE INDEX ix_matches_remittance_id ON matches(remittance_id);
CREATE INDEX ix_matches_matched_at ON matches(matched_at);

CREATE TABLE cas_adjustments_new (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    remittance_id TEXT NOT NULL,
    remittance_batch_id TEXT NOT NULL,
    group_code TEXT NOT NULL,
    reason_code TEXT NOT NULL,
    amount NUMERIC(12, 2) NOT NULL,
    quantity NUMERIC(10, 2),
    service_line_payment_id INTEGER REFERENCES service_line_payments(id) ON DELETE SET NULL,
    FOREIGN KEY (remittance_batch_id, remittance_id) REFERENCES remittances(batch_id, id) ON DELETE CASCADE
);
INSERT INTO cas_adjustments_new
    SELECT ca.id, ca.remittance_id, r.batch_id, ca.group_code, ca.reason_code,
           ca.amount, ca.quantity, ca.service_line_payment_id
    FROM cas_adjustments ca
    JOIN remittances r ON r.id = ca.remittance_id;
DROP TABLE cas_adjustments;
ALTER TABLE cas_adjustments_new RENAME TO cas_adjustments;
CREATE INDEX ix_cas_adjustments_remittance_id ON cas_adjustments(remittance_id);
CREATE INDEX ix_cas_adjustments_service_line_payment_id ON cas_adjustments(service_line_payment_id);

CREATE TABLE service_line_payments_new (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    remittance_id TEXT NOT NULL,
    remittance_batch_id TEXT NOT NULL,
    line_number INTEGER NOT NULL,
    procedure_qualifier VARCHAR(4) NOT NULL,
    procedure_code VARCHAR(16) NOT NULL,
    modifiers_json TEXT NOT NULL DEFAULT '[]',
    charge NUMERIC(12, 2) NOT NULL,
    payment NUMERIC(12, 2) NOT NULL,
    units NUMERIC(10, 2),
    unit_type VARCHAR(8),
    service_date DATE,
    ref_benefit_plan VARCHAR(64),
    superseded_by_id INTEGER REFERENCES service_line_payments(id) ON DELETE SET NULL,
    FOREIGN KEY (remittance_batch_id, remittance_id) REFERENCES remittances(batch_id, id) ON DELETE CASCADE
);
INSERT INTO service_line_payments_new
    SELECT slp.id, slp.remittance_id, r.batch_id, slp.line_number,
           slp.procedure_qualifier, slp.procedure_code, slp.modifiers_json,
           slp.charge, slp.payment, slp.units, slp.unit_type, slp.service_date,
           slp.ref_benefit_plan, slp.superseded_by_id
    FROM service_line_payments slp
    JOIN remittances r ON r.id = slp.remittance_id;
DROP TABLE service_line_payments;
ALTER TABLE service_line_payments_new RENAME TO service_line_payments;
CREATE INDEX ix_service_line_payments_remittance_id ON service_line_payments(remittance_id);
CREATE INDEX ix_service_line_payments_procedure_code_date ON service_line_payments(procedure_code, service_date);

CREATE TABLE line_reconciliations_new (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    claim_id VARCHAR(64) NOT NULL,
    batch_id VARCHAR(64) NOT NULL,
    claim_service_line_number INTEGER,
    service_line_payment_id INTEGER REFERENCES service_line_payments(id) ON DELETE SET NULL,
    status VARCHAR(16) NOT NULL,
    match_score INTEGER,
    reconciled_at TIMESTAMP NOT NULL,
    FOREIGN KEY (batch_id, claim_id) REFERENCES claims(batch_id, id) ON DELETE CASCADE
);
INSERT INTO line_reconciliations_new
    SELECT lr.id, lr.claim_id, c.batch_id, lr.claim_service_line_number,
           lr.service_line_payment_id, lr.status, lr.match_score, lr.reconciled_at
    FROM line_reconciliations lr
    JOIN claims c ON c.id = lr.claim_id;
DROP TABLE line_reconciliations;
ALTER TABLE line_reconciliations_new RENAME TO line_reconciliations;
CREATE INDEX ix_line_reconciliations_claim_id ON line_reconciliations(claim_id);
CREATE INDEX ix_line_reconciliations_claim_service_line_number ON line_reconciliations(claim_service_line_number);
CREATE INDEX ix_line_reconciliations_service_line_payment_id ON line_reconciliations(service_line_payment_id);
