/**
 * X12 file-kind auto-detection helper.
 *
 * SP35: the Upload page used to default to "837p" silently — dropping an 835
 * file on the page while the dropdown still said "837p" routed the file to
 * /api/parse-837, which silently persisted an empty batch. Layer A of the
 * defense-in-depth fix is to detect the kind from the file's first few KB
 * and switch the dropdown automatically before the user clicks Parse.
 *
 * Layer B is the server-side envelope check (cyclone.api._transaction_set_id_from_segments).
 * This helper is purely advisory: if it returns null, the UI keeps whatever
 * the user picked and the server guard rejects with a clean 400 if it's wrong.
 *
 * Detection is intentionally cheap: read the first ~4KB and look for the ST
 * segment (or the bare TA1 segment, which has no ST envelope). The full file
 * is never loaded — X12 envelopes are always within the first ISA segment.
 */

import type { ParsedBatchKind } from "@/types";

/** Number of bytes to read from the start of the file for detection. */
const DETECT_BYTES = 4096;

export type DetectedKind = ParsedBatchKind | "999" | "277ca" | "ta1" | "unknown";

export function detectKindFromText(text: string): DetectedKind {
  // Strip whitespace and look for the ST segment.
  // X12 envelopes are always within the first segment, so we don't need
  // to scan far. The segment terminator is `~` (per the ISA segment,
  // which we also don't need to parse here — we only care which kind of
  // payload follows).
  //
  // We do a simple substring search rather than full tokenization because:
  //   - Detection must run before the user even knows if their file is
  //     valid X12. If we ran `tokenize()` and the file has a malformed
  //     ISA, we'd throw and the UI couldn't show a helpful message.
  //   - A simple prefix match on `ST*<kind>*` is enough to disambiguate
  //     every kind the Upload page might receive. False positives are
  //     caught by the server-side guard.
  const upper = text.toUpperCase();

  // 277CA uses ST*277* or ST*277CA* (per parse_277ca.py line 13 comment).
  // Check 277CA before 277 because ST*277CA contains "277".
  if (upper.includes("ST*277CA*")) return "277ca";
  if (upper.includes("ST*277*")) return "277ca";

  // 837P accepts ST*837* or ST*837P* (the trailing P is the professional
  // claim qualifier, but the ISA envelope alone tells you it's a
  // professional file). Check 837P before 837 for the same reason.
  if (upper.includes("ST*837P*")) return "837p";
  if (upper.includes("ST*837*")) return "837p";

  // 835 has ST*835* — no other qualifier in common use.
  if (upper.includes("ST*835*")) return "835";

  // 999 ACK has ST*999* — no qualifier.
  if (upper.includes("ST*999*")) return "999";

  // TA1 has no ST envelope. The interchange-ack segment is the bare TA1*
  // immediately after ISA/IEA. Match the segment header.
  if (/\bTA1\*/.test(upper)) return "ta1";

  return "unknown";
}

/**
 * Browser-side helper: read the first DETECT_BYTES from a File and detect
 * its kind. Returns a Promise so it composes naturally with FileReader /
 * Blob.slice. Returns "unknown" on read error so the caller can keep the
 * user's current selection and let the server guard surface a clean 400.
 */
export async function detectKindFromFile(file: File): Promise<DetectedKind> {
  try {
    const blob = file.slice(0, DETECT_BYTES);
    const text = await blob.text();
    return detectKindFromText(text);
  } catch {
    return "unknown";
  }
}

/**
 * Map a DetectedKind to the ParsedBatchKind the Upload dropdown supports.
 * Returns null for kinds the Upload page can't ingest (999, 277CA, TA1) —
 * the UI then surfaces a "this file isn't supported here" hint instead of
 * silently misrouting it. The server-side guards in cyclone.api still
 * catch any escape.
 */
export function detectedKindToParsedBatchKind(kind: DetectedKind): ParsedBatchKind | null {
  switch (kind) {
    case "837p":
      return "837p";
    case "835":
      return "835";
    default:
      return null;
  }
}