// ---------------------------------------------------------------------------
// UI-shaped types (used by the existing pages; backed by in-memory sample data
// when no API is configured). Keep these stable — the rest of the app reads
// from them.
// ---------------------------------------------------------------------------

export type ClaimStatus =
  | "draft"
  | "submitted"
  | "accepted"
  | "denied"
  | "paid"
  | "pending";

export type RemittanceStatus = "received" | "posted" | "reconciled";

export type ActivityKind =
  | "claim_submitted"
  | "claim_paid"
  | "claim_denied"
  | "claim_accepted"
  | "remit_received"
  | "provider_added"
  // Emitted by `cyclone.store.record_manual_match` when an operator
  // pairs a remit to a claim by hand. Surface as a neutral event on
  // the Activity Log; do NOT treat it as a status change.
  | "manual_match";

export interface Claim {
  id: string;
  patientName: string;
  providerNpi: string;
  payerName: string;
  cptCode: string;
  billedAmount: number;
  receivedAmount: number;
  status: ClaimStatus;
  submissionDate: string;
  denialReason?: string;
}

export interface Provider {
  npi: string;
  name: string;
  taxId: string;
  address: string;
  city: string;
  state: string;
  zip: string;
  phone: string;
  claimCount: number;
  outstandingAr: number;
  /**
   * SP21 Task 1.6: populated by the extended `GET /api/config/providers/{npi}`
   * endpoint. Top 10 claims for this provider by `submissionDate` desc.
   * Optional so legacy callers (in-memory sample data) keep working
   * without an API round-trip.
   */
  recent_claims?: ClaimSummary[];
  /**
   * SP21 Task 1.6: populated by the extended `GET /api/config/providers/{npi}`
   * endpoint. Top 10 activity events (by `ts` desc) joined to claims
   * owned by this provider via `claim_id`.
   */
  recent_activity?: ActivityEvent[];
}

/**
 * SP21 Task 1.6: slim claim projection returned by
 * `GET /api/config/providers/{npi}.recent_claims`. Mirrors the wire
 * shape of `store.iter_claims(provider_npi=...)` 1:1 (camelCase,
 * superset of the legacy `Claim` interface).
 */
export interface ClaimSummary {
  id: string;
  state: string;
  billedAmount: number;
  patientName: string;
  providerNpi: string;
  payerName: string;
  cptCode: string;
  submissionDate: string;
  parsedAt: string;
  status: string;
  matchedRemittanceId?: string | null;
  batchId: string;
  receivedAmount?: number;
  denialReason?: string | null;
}

/**
 * SP21 Task 1.6: one row in `recent_activity`. Mirrors the Python
 * ORM `ActivityEvent` (snake_case fields rewritten to camelCase for
 * the wire). `payload` carries the same dict the recorder wrote at
 * the time of the event (message, npi, amount, etc.).
 */
export interface ActivityEvent {
  id: number;
  ts: string; // ISO Z
  kind: string;
  batchId: string | null;
  claimId: string | null;
  remittanceId: string | null;
  payload: Record<string, unknown>;
}

export interface Remittance {
  id: string;
  claimId: string;
  payerName: string;
  paidAmount: number;
  adjustmentAmount: number;
  receivedDate: string;
  checkNumber: string;
  status: RemittanceStatus;
  denialReason?: string | null;
  validationWarnings?: string[];
  /**
   * Persisted CAS adjustment rows, labeled by the backend via the bundled
   * CARC dictionary. Optional — the list endpoint omits it to keep the
   * payload small; the detail endpoint (`/api/remittances/{id}`) populates it.
   */
  adjustments?: CasAdjustment[];
  /**
   * SP7: per-line 835 SVC composites on this remittance. Ordered by
   * ``lineNumber``. Each SLP carries the per-line CAS adjustments under
   * ``serviceLinePayments[i].adjustments`` via the line-reconciliation
   * endpoint, or via a follow-up CAS query in the remit drawer.
   */
  serviceLinePayments?: ServiceLinePayment[];
  /**
   * SP7: CLP-level (claim-level) CAS bucket — adjustments that were
   * not attached to any SVC composite. Powers the ClaimLevelAdjustments
   * panel in the remit drawer.
   */
  claimLevelAdjustments?: RemittanceClaimLevelAdjustment[];
}

/**
 * One Claim Adjustment Segment (CAS) row, labeled.
 * Source: backend `cyclone.parsers.cas_codes.reason_label(group, reason)`.
 */
export interface CasAdjustment {
  group: string;       // "CO", "PR", "OA", "PI", "CR"
  reason: string;      // "45", "1", etc.
  label: string;       // "Charge exceeds fee schedule"
  amount: number;
  quantity: number | null;
}

export interface Activity {
  id: string;
  kind: ActivityKind;
  message: string;
  timestamp: string;
  npi?: string;
  amount?: number;
  /**
   * SP21 Task 2.5: read from the backend `ActivityEvent.claim_id` so
   * the Dashboard "Recent activity" card can route a click on a
   * `claim_*` event to the matching claim drawer (via
   * `src/lib/event-routing.ts`). Populated only for events that
   * correspond to a specific claim; `null` for orphan events such as
   * `remit_received` or `provider_added`.
   */
  claimId?: string | null;
  /**
   * SP21 Task 2.5: read from the backend `ActivityEvent.remittance_id`.
   * Populated only for events that reference a specific remittance
   * (e.g. `remit_received`); `null` otherwise. The Dashboard toast
   * covers the Phase 2 case where this routes to a not-yet-built
   * `RemitDrawer` (Phase 4).
   */
  remittanceId?: string | null;
}

// ---------------------------------------------------------------------------
// Backend-aligned types (snake_case to match FastAPI JSON output 1:1).
// These mirror the Pydantic models in
//   backend/src/cyclone/parsers/models.py        (837P)
//   backend/src/cyclone/parsers/models_835.py    (835 ERA)
// Numeric fields are strings (Decimals serialized); dates are ISO strings.
// ---------------------------------------------------------------------------

// --- 837P ------------------------------------------------------------------

export interface Address {
  line1: string;
  line2?: string | null;
  city: string;
  state: string;
  zip: string;
}

export interface BillingProvider {
  name: string;
  npi: string;
  tax_id?: string | null;
  address?: Address | null;
}

export interface Subscriber {
  first_name: string;
  last_name: string;
  member_id: string;
  dob?: string | null;
  gender?: "M" | "F" | "U" | null;
  address?: Address | null;
}

export interface Payer {
  name: string;
  id: string;
}

export interface ClaimHeader {
  claim_id: string;
  total_charge: string; // Decimal serialized
  place_of_service?: string | null;
  facility_code_qualifier?: string | null;
  frequency_code?: string | null;
  provider_signature?: string | null;
  assignment?: string | null;
  release_of_info?: string | null;
  prior_auth?: string | null;
}

export interface Diagnosis {
  code: string;
  qualifier?: string | null;
}

export interface Procedure {
  qualifier: string;
  code: string;
  modifiers: string[];
}

export interface ServiceLine {
  line_number: number;
  procedure: Procedure;
  charge: string; // Decimal serialized
  unit_type?: string | null;
  units?: string | null; // Decimal serialized
  place_of_service?: string | null;
  service_date?: string | null;
  provider_reference?: string | null;
}

export interface ValidationIssue {
  rule: string;
  severity: "error" | "warning";
  message: string;
  segment_index?: number | null;
}

export interface ValidationReport {
  passed: boolean;
  errors: ValidationIssue[];
  warnings: ValidationIssue[];
}

export interface Envelope {
  sender_id: string;
  receiver_id: string;
  control_number: string;
  transaction_date: string; // ISO date
  transaction_time?: string | null;
  implementation_guide?: string | null;
}

export interface BatchSummary {
  input_file: string;
  control_number?: string | null;
  transaction_date?: string | null;
  total_claims: number;
  passed: number;
  failed: number;
  failed_claim_ids: string[];
  issues_by_rule: Record<string, number>;
  output_dir?: string | null;
}

export interface ClaimOutput {
  claim_id: string;
  control_number: string;
  transaction_date: string; // ISO date
  billing_provider: BillingProvider;
  subscriber: Subscriber;
  payer: Payer;
  claim: ClaimHeader;
  diagnoses: Diagnosis[];
  service_lines: ServiceLine[];
  validation: ValidationReport;
  raw_segments: string[][];
}

export interface ParseResult837 {
  envelope: Envelope | null;
  claims: ClaimOutput[];
  summary: BatchSummary;
}

// --- 835 ERA ---------------------------------------------------------------

export interface FinancialInfo {
  handling_code: string;
  paid_amount: string; // Decimal serialized
  credit_debit_flag: string;
  payment_method?: string | null;
  payer_tax_id?: string | null;
  payment_date?: string | null; // ISO date
}

export interface ReassociationTrace {
  trace_type_code: string;
  trace_number: string;
  originating_company_id: string;
  payer_id?: string | null;
}

export interface Payer835 {
  name: string;
  id?: string | null;
  address?: Address | null;
  contact_url?: string | null;
}

export interface Payee835 {
  name: string;
  npi: string;
  address?: Address | null;
}

export interface ClaimAdjustment {
  group_code: string;
  reason_code: string;
  amount: string; // Decimal serialized
  quantity?: string | null;
}

export interface ServicePayment {
  line_number: number;
  procedure_qualifier: string;
  procedure_code: string;
  modifiers: string[];
  charge: string;
  payment: string;
  units?: string | null;
  unit_type?: string | null;
  service_date?: string | null;
  adjustments: ClaimAdjustment[];
  ref_benefit_plan?: string | null;
}

export interface ClaimPayment {
  payer_claim_control_number: string;
  status_code: string;
  status_label?: string | null;
  total_charge: string;
  total_paid: string;
  patient_responsibility?: string | null;
  claim_filing_indicator?: string | null;
  original_claim_id?: string | null;
  facility_type?: string | null;
  frequency_code?: string | null;
  per_diem_covered_days?: string | null;
  ref_benefit_plan?: string | null;
  service_payments: ServicePayment[];
  raw_segments: string[][];
}

export interface ParseResult835 {
  envelope: Envelope;
  financial_info: FinancialInfo;
  trace: ReassociationTrace;
  payer: Payer835;
  payee: Payee835;
  claims: ClaimPayment[];
  summary: BatchSummary;
  validation?: ValidationReport | null;
}

// --- NDJSON streaming ------------------------------------------------------
// The backend emits one JSON object per line during an NDJSON stream:
//   { "type": "<event>", "data": <object> }
// See backend/src/cyclone/api.py:_ndjson_stream* for the source of truth.

export type NdjsonEventType =
  | "envelope"
  | "claim"
  | "claim_payment"
  | "financial_info"
  | "trace"
  | "payer"
  | "payee"
  | "summary";

export interface NdjsonEvent<T = unknown> {
  type: NdjsonEventType;
  data: T;
}

// --- Parsed-batch slice (used by the Upload page + store) ------------------

export type ParsedBatchKind = "837p" | "835";

export interface ParsedBatch {
  id: string;
  kind: ParsedBatchKind;
  inputFilename: string;
  parsedAt: string; // ISO timestamp
  claimCount: number;
  passed: number;
  failed: number;
  claimIds: string[];
  summary: BatchSummary;
}

// ---------------------------------------------------------------------------
// Reconciliation (7-state claim model + match audit trail)
// Mirrors cyclone.db.ClaimState (Python enum) 1:1. The reconciliation page
// uses these to drive the unmatched bucket and the manual-match UI.
// ---------------------------------------------------------------------------

export type ClaimState =
  | "submitted"
  | "received"
  | "paid"
  | "partial"
  | "denied"
  | "reconciled"
  | "reversed";

export const CLAIM_STATES: ClaimState[] = [
  "submitted",
  "received",
  "paid",
  "partial",
  "denied",
  "reconciled",
  "reversed",
];

export interface Match {
  id: number;
  claim_id: string;
  remittance_id: string;
  strategy: "auto" | "manual";
  matched_at: string;
  prior_claim_state?: ClaimState | null;
  is_reversal: boolean;
}

// Mirrors `cyclone.store.to_ui_claim_from_orm` (backend/src/cyclone/store.py).
// Fields the backend does not currently emit are optional so the type
// matches the actual wire shape (was previously assumed but never true).
export interface UnmatchedClaim {
  id: string;
  patientName: string;
  billedAmount: number;
  providerNpi: string | null;
  payerName?: string;
  payerId?: string | null;
  serviceDate?: string | null;
  cptCode?: string;
  submissionDate?: string;
  state: ClaimState;
}

// Mirrors `cyclone.store.list_unmatched` (backend/src/cyclone/store.py) for
// the remittances payload. `status` is the 3-state remittance lifecycle
// ("received" | "posted" | "reconciled"); the previous `statusCode` field
// was a confusion with the 835 claim-status code and never existed on the
// wire. Fields the backend omits are optional.
export interface UnmatchedRemittance {
  id: string;
  payerClaimControlNumber: string;
  payerName?: string;
  status: string;
  paidAmount: number;
  adjustmentAmount: number;
  batchId: string;
  receivedDate?: string;
  parsedAt?: string;
  claimId?: string;
  denialReason?: string | null;
  validationWarnings?: string[];
  isReversal?: boolean;
  totalCharge?: number;
  serviceDate?: string | null;
}

export interface UnmatchedResponse {
  claims: UnmatchedClaim[];
  remittances: UnmatchedRemittance[];
}

export interface MatchResponse {
  claim: UnmatchedClaim;
  match: { id: number; strategy: "auto" | "manual" };
}

// ---------------------------------------------------------------------------
// 999 ACKs (SP3 P3 T16)
// Mirrors the `acks` table (cyclone.db.Ack) and the `/api/acks` response.
// ---------------------------------------------------------------------------

/**
 * One persisted 999 ACK row, camelCased for the UI. The backend
 * (snake_case) is re-shaped in `src/hooks/useAcks.ts` via the
 * `mapAck` helper, so this interface matches what the page actually
 * consumes.
 */
export interface Ack {
  id: number;
  sourceBatchId: string;
  acceptedCount: number;
  rejectedCount: number;
  receivedCount: number;
  ackCode: "A" | "E" | "R" | "P";
  parsedAt: string;
}

// ---------------------------------------------------------------------------
// SP4 claim detail drawer types (Task 4).
// Mirrors the JSON shape of `GET /api/claims/{claim_id}` 1:1 — camelCase
// keys, numeric money fields (coerced from Decimal on the backend), ISO
// timestamps with a trailing `Z`. Distinct from the parser-aligned
// snake_case types above (e.g. ``BillingProvider``, ``ServiceLine``) which
// describe the EDI parse tree; these describe the UI-ready detail payload.
// The ``ClaimDetail*`` prefix keeps the two flavors side-by-side without
// a TypeScript duplicate-identifier collision.
// ---------------------------------------------------------------------------

export interface ClaimDetailDiagnosis {
  code: string;
  qualifier: string | null;
}

export interface ClaimDetailServiceLine {
  lineNumber: number;
  procedureQualifier: string;
  procedureCode: string;
  modifiers: string[];
  charge: number;
  units: number | null;
  unitType: string | null;
  serviceDate: string | null; // ISO date YYYY-MM-DD or null
}

/**
 * The backend can return ``{}`` for a missing address (see
 * ``store._address_to_ui``); callers that want a populated address must
 * branch on ``Object.keys(addr).length === 0`` before reading fields.
 */
export interface ClaimDetailAddress {
  line1: string;
  line2: string | null;
  city: string;
  state: string;
  zip: string;
}

export interface ClaimDetailBillingProvider {
  name: string;
  npi: string;
  taxId: string;
  address: ClaimDetailAddress | Record<string, never>;
}

export interface ClaimDetailSubscriber {
  firstName: string;
  lastName: string;
  memberId: string;
  dob: string | null;
  gender: string | null;
}

export interface ClaimDetailPayer {
  name: string;
  id: string;
}

export interface ClaimDetailParties {
  billingProvider: ClaimDetailBillingProvider;
  subscriber: ClaimDetailSubscriber;
  payer: ClaimDetailPayer;
}

export interface ClaimDetailValidationIssue {
  rule: string;
  severity: "error" | "warning";
  message: string;
}

export interface ClaimDetailValidation {
  passed: boolean;
  errors: ClaimDetailValidationIssue[];
  warnings: ClaimDetailValidationIssue[];
}

export interface ClaimDetailMatchedRemittance {
  id: string;
  totalPaid: number;
  /** Mirrors the same `"received" | "reconciled"` mapping as the list endpoint. */
  status: string;
  receivedAt: string; // ISO Z
  /**
   * SP7: per-line counts for the badge. Optional for backward
   * compatibility — older claim-detail responses (pre-SP7) won't have
   * these fields. The card omits the badge when undefined.
   */
  matchedLines?: number;
  totalLines?: number;
}

export interface ClaimDetailStateHistoryEvent {
  kind: string;
  ts: string; // ISO Z (most-recent-first, capped at 50 by the backend)
  batchId: string | null;
  remittanceId: string | null;
}

/**
 * One claim with the full drawer context: header, state, parties,
 * validation, service lines, diagnoses, raw segments, recent
 * ``stateHistory`` (most-recent-first, capped at 50), and a populated
 * ``matchedRemittance`` block when the claim has been paired with an
 * ERA. Mirrors ``CycloneStore.get_claim_detail`` (backend/src/cyclone/store.py)
 * and the ``GET /api/claims/{claim_id}`` endpoint contract.
 */
export interface ClaimDetail {
  id: string;
  batchId: string;
  state: string;
  stateLabel: string;
  billedAmount: number;
  patientName: string;
  providerNpi: string;
  providerName: string;
  payerName: string;
  payerId: string;
  submissionDate: string; // ISO Z
  serviceDateFrom: string | null;
  serviceDateTo: string | null;
  parsedAt: string; // ISO Z
  diagnoses: ClaimDetailDiagnosis[];
  serviceLines: ClaimDetailServiceLine[];
  parties: ClaimDetailParties;
  validation: ClaimDetailValidation;
  rawSegments: string[][];
  matchedRemittance: ClaimDetailMatchedRemittance | null;
  stateHistory: ClaimDetailStateHistoryEvent[];
  /**
   * SP7: per-line reconciliation slim projection. Parallel to
   * ``serviceLines`` and indexed by ``lineNumber``. Populated by the
   * claim-detail endpoint so the ServiceLinesTable can render Paid +
   * Adjustments columns without a second fetch. ``paid`` /
   * ``adjustmentsSum`` are null when the line has no matched 835 SVC
   * composite.
   */
  lineReconciliation?: ClaimDetailLineReconciliation[];
}

// ---------------------------------------------------------------------------
// Side-by-side batch diff (SP3 P4 / T18)
// Mirrors `GET /api/batch-diff?a=<batch_id>&b=<batch_id>` (see
// `backend/src/cyclone/batch_diff.py` and `backend/src/cyclone/api.py`).
// The contract is deliberately small: no raw segments, no service-line
// details — just the summary fields the operator needs to spot the
// deltas between two parsed files at a glance.
// ---------------------------------------------------------------------------

export type BatchKind = "837p" | "835";

export interface BatchDiffSideMeta {
  id: string;
  kind: BatchKind;
  parsedAt: string; // ISO Z
  inputFilename: string;
  claimCount: number;
}

/**
 * One claim (or remittance) row projected for the diff view. Carries
 * just enough context to render side-by-side without the EDI parse
 * tree. ``status`` carries the validation-derived label for 837P
 * (``submitted`` / ``pending`` / ``draft`` / ``denied``) and the CLP02
 * code/label for 835.
 */
export interface BatchClaimDiffSummary {
  claim_id: string;
  patient_name: string;
  total_charge: number;
  service_date: string | null;
  status: string;
  cpt_codes: string[];
}

/**
 * One changed field between the two sides of a claim. ``field``
 * identifies which column changed (``status`` / ``total_charge`` /
 * ``service_date`` / ``cpt_codes``); ``a`` and ``b`` are the values
 * from each side, rendered verbatim.
 */
export interface BatchDiffFieldDiff {
  field: "status" | "total_charge" | "service_date" | "cpt_codes";
  a: string | number | string[] | null;
  b: string | number | string[] | null;
}

export interface BatchDiffChangedRow {
  a: BatchClaimDiffSummary;
  b: BatchClaimDiffSummary;
  diff: BatchDiffFieldDiff[];
}

export interface BatchDiffSummary {
  addedCount: number;
  removedCount: number;
  changedCount: number;
  unchangedCount: number;
}

/**
 * Full response from `GET /api/batch-diff`. The three bucket arrays
 * (``added`` / ``removed`` / ``changed``) are always present (never
 * absent) so the UI can index unconditionally; ``summary`` is
 * precomputed by the backend so the four count cards don't have to
 * derive them on the client.
 */
export interface BatchDiff {
  a: BatchDiffSideMeta;
  b: BatchDiffSideMeta;
  added: BatchClaimDiffSummary[];
  removed: BatchClaimDiffSummary[];
  changed: BatchDiffChangedRow[];
  summary: BatchDiffSummary;
}

// ---------------------------------------------------------------------------
// SP7 — Per-Service-Line Adjustment Audit types.
// See docs/superpowers/specs/2026-06-20-cyclone-line-reconciliation-design.md
// ---------------------------------------------------------------------------

/** One 835 SVC composite (Loop 2110) projected to wire shape. */
export interface ServiceLinePayment {
  id: number;
  lineNumber: number;
  procedureQualifier: string;
  procedureCode: string;
  modifiers: string[];
  charge: string;
  payment: string;
  units: string | null;
  unitType: string | null;
  serviceDate: string | null;
}

/** Status of one line on either side of a reconciliation. */
export type LineReconciliationStatus =
  | "matched"
  | "unmatched_835_only"
  | "unmatched_837_only"
  | "superseded";

/** Slim per-line projection embedded in `GET /api/claims/{id}`. */
export interface ClaimDetailLineReconciliation {
  lineNumber: number;
  status: LineReconciliationStatus;
  paid: string | null;
  adjustmentsSum: string | null;
}

/** Summary block at the top of the line-reconciliation response. */
export interface LineReconciliationSummary {
  billedTotal: string;
  paidTotal: string;
  adjustmentTotal: string;
  matchedLines: number;
  totalLines: number;
}

/** One line in the line-reconciliation response. */
export interface LineReconciliationRow {
  claimServiceLine: {
    lineNumber: number;
    procedureQualifier: string;
    procedureCode: string;
    modifiers: string[];
    charge: string;
    units: string | null;
    unitType: string | null;
    serviceDate: string | null;
  } | null;
  serviceLinePayment: ServiceLinePayment | null;
  status: LineReconciliationStatus;
  adjustments: Array<{
    groupCode: string;
    reasonCode: string;
    amount: string;
  }>;
}

/** Top-level shape of `GET /api/claims/{id}/line-reconciliation`. */
export interface LineReconciliationResponse {
  claimId: string;
  summary: LineReconciliationSummary;
  lines: LineReconciliationRow[];
}

/** One CLP-level (claim-level) CAS adjustment on a remittance. */
export interface RemittanceClaimLevelAdjustment {
  id: number;
  groupCode: string;
  reasonCode: string;
  amount: string;
  quantity: string | null;
}

/** Counts embedded in inbox-lane rows for the `MatchedRemitCard` badge. */
export interface MatchedRemittanceLineCounts {
  matchedLines: number;
  totalLines: number;
}

/** Inbox-lane claim row carrying the matched-remittance block. */
export interface InboxClaimRow {
  id: string;
  kind: "claim" | "remit";
  patientControlNumber: string;
  chargeAmount: number | null;
  payerId: string | null;
  providerNpi: string | null;
  state: string;
  rejectionReason: string | null;
  rejectedAt: string | null;
  serviceDateFrom: string | null;
  score: number | null;
  scoreTier: string | null;
  scoreBreakdown: {
    patient: number;
    date: number;
    amount: number;
    provider: number;
  } | null;
  matchedRemittance: {
    id: string;
    matchedLines: number;
    totalLines: number;
  } | null;
}
