// IMP-92 u5 — Frontend AI repair operational-only formatter test surface.
//
// Scope (Stage 2 unit u5 contract):
//   1) formatAiRepairHumanReviewMessage(...) surfaces a user-facing toast
//      ONLY on the three operational Anthropic API error kinds (quota /
//      billing / auth) classified by Step 12 u2
//      (classify_operational_error) and aggregated through u3
//      ai_repair_status.api_error_kinds.
//   2) Non-operational AI failures (validation / coverage_violated /
//      unsupported_kind / generic "other") return null so the
//      auto-pipeline stays silent per feedback_auto_pipeline_first and
//      the #84 operational-vs-non-operational replacement-plan contract.
//   3) Replaces the prior IMP-47B u11 surface — previously rendered toasts
//      for error / coverage_violated / unsupported_kind. After IMP-92 the
//      ONLY operational reaches the user; non-operational stays silent.
//
// Pure-function unit test (no React Testing Library required — vitest is
// already in devDependencies; @testing-library/* is NOT installed). The
// Home.tsx wiring is a 2-line site (`Home.tsx:438`) that calls this helper
// after `setRunMeta(...)`; covering the helper covers the user-visible
// message text directly without DOM rendering.
//
// The test file path is preserved from IMP-47B u11 (Stage 2 plan
// `Front/client/tests/imp47b_human_review_toast.test.tsx`); the assertions
// inside reflect the IMP-92 u5 operational-only contract.

import { describe, it, expect } from "vitest";
import {
  formatAiRepairHumanReviewMessage,
  type AiRepairStatus,
} from "../src/services/designAgentApi";

const baseCounts = {
  total: 0,
  applied: 0,
  no_proposal: 0,
  no_zone_match: 0,
  unsupported_kind: 0,
  error: 0,
};

const zeroKinds = { quota: 0, billing: 0, auth: 0, other: 0 };

describe("formatAiRepairHumanReviewMessage (IMP-92 u5 — operational-only)", () => {
  it("returns null when ai_repair_status is null / undefined", () => {
    expect(formatAiRepairHumanReviewMessage(null)).toBeNull();
    expect(formatAiRepairHumanReviewMessage(undefined)).toBeNull();
  });

  it("returns null on success / no-AI path (no operational kind present)", () => {
    const ok: AiRepairStatus = {
      status: "ok",
      counts: { ...baseCounts },
      api_error_kinds: { ...zeroKinds },
      unsupported_kind_records: [],
      error_records: [],
      coverage_status: "ok",
      dropped_section_ids: [],
      human_review_required: false,
    };
    expect(formatAiRepairHumanReviewMessage(ok)).toBeNull();

    const applied: AiRepairStatus = {
      ...ok,
      status: "applied",
      counts: { ...baseCounts, total: 1, applied: 1 },
    };
    expect(formatAiRepairHumanReviewMessage(applied)).toBeNull();
  });

  it("surfaces quota operational alert (Anthropic 429 / RateLimitError)", () => {
    const ai: AiRepairStatus = {
      status: "error",
      counts: { ...baseCounts, total: 2, error: 2 },
      api_error_kinds: { quota: 2, billing: 0, auth: 0, other: 0 },
      unsupported_kind_records: [],
      error_records: [
        {
          unit_index: 0,
          source_section_ids: ["03-1"],
          error: "RateLimitError: rate_limit_exceeded",
          api_error_kind: "quota",
        },
        {
          unit_index: 1,
          source_section_ids: ["03-2"],
          error: "RateLimitError: rate_limit_exceeded",
          api_error_kind: "quota",
        },
      ],
      coverage_status: "ok",
      dropped_section_ids: [],
      human_review_required: true,
    };
    const msg = formatAiRepairHumanReviewMessage(ai);
    expect(msg).not.toBeNull();
    expect(msg).toContain("API quota");
    expect(msg).toContain("충전 필요");
    expect(msg).toContain("2");
  });

  it("surfaces billing operational alert (Anthropic 402 / PermissionDeniedError)", () => {
    const ai: AiRepairStatus = {
      status: "error",
      counts: { ...baseCounts, total: 1, error: 1 },
      api_error_kinds: { quota: 0, billing: 1, auth: 0, other: 0 },
      unsupported_kind_records: [],
      error_records: [
        {
          unit_index: 0,
          source_section_ids: ["03-1"],
          error: "PermissionDeniedError: insufficient credits",
          api_error_kind: "billing",
        },
      ],
      coverage_status: "ok",
      dropped_section_ids: [],
      human_review_required: true,
    };
    const msg = formatAiRepairHumanReviewMessage(ai);
    expect(msg).not.toBeNull();
    expect(msg).toContain("API billing");
    expect(msg).toContain("결제 정보 확인");
    expect(msg).toContain("1");
  });

  it("surfaces auth operational alert (Anthropic 401 / AuthenticationError)", () => {
    const ai: AiRepairStatus = {
      status: "error",
      counts: { ...baseCounts, total: 1, error: 1 },
      api_error_kinds: { quota: 0, billing: 0, auth: 1, other: 0 },
      unsupported_kind_records: [],
      error_records: [
        {
          unit_index: 0,
          source_section_ids: ["03-1"],
          error: "AuthenticationError: invalid x-api-key",
          api_error_kind: "auth",
        },
      ],
      coverage_status: "ok",
      dropped_section_ids: [],
      human_review_required: true,
    };
    const msg = formatAiRepairHumanReviewMessage(ai);
    expect(msg).not.toBeNull();
    expect(msg).toContain("API key 무효");
    expect(msg).toContain(".env");
    expect(msg).toContain("1");
  });

  it("returns null on generic non-operational 'other' API error (silent)", () => {
    const ai: AiRepairStatus = {
      status: "error",
      counts: { ...baseCounts, total: 1, error: 1 },
      api_error_kinds: { quota: 0, billing: 0, auth: 0, other: 1 },
      unsupported_kind_records: [],
      error_records: [
        {
          unit_index: 0,
          source_section_ids: ["03-1"],
          error: "ValidationError: proposal failed schema",
          api_error_kind: "other",
        },
      ],
      coverage_status: "ok",
      dropped_section_ids: [],
      human_review_required: true,
    };
    expect(formatAiRepairHumanReviewMessage(ai)).toBeNull();
  });

  it("returns null on coverage_violated (non-operational, silent)", () => {
    const ai: AiRepairStatus = {
      status: "coverage_violated",
      counts: { ...baseCounts, total: 1, applied: 1 },
      api_error_kinds: { ...zeroKinds },
      unsupported_kind_records: [],
      error_records: [],
      coverage_status: "violated",
      dropped_section_ids: ["03-2"],
      human_review_required: true,
    };
    expect(formatAiRepairHumanReviewMessage(ai)).toBeNull();
  });

  it("returns null on unsupported_kind (non-operational, silent)", () => {
    const ai: AiRepairStatus = {
      status: "unsupported_kind",
      counts: { ...baseCounts, total: 1, unsupported_kind: 1 },
      api_error_kinds: { ...zeroKinds },
      unsupported_kind_records: [
        {
          unit_index: 0,
          source_section_ids: ["03-1"],
          apply_status: "unsupported_kind_for_reject_route:builder_options_patch",
        },
      ],
      error_records: [],
      coverage_status: "ok",
      dropped_section_ids: [],
      human_review_required: true,
    };
    expect(formatAiRepairHumanReviewMessage(ai)).toBeNull();
  });

  it("returns null on legacy ai_repair_status without api_error_kinds (pre-u3 runs)", () => {
    // Backward-compat: payloads emitted before u3 plumbing landed don't
    // carry api_error_kinds. Operational-only contract treats the absence
    // as "no operational signal" → silent (no toast).
    const legacy: AiRepairStatus = {
      status: "error",
      counts: { ...baseCounts, total: 1, error: 1 },
      // api_error_kinds intentionally omitted
      unsupported_kind_records: [],
      error_records: [
        { unit_index: 0, source_section_ids: ["03-1"], error: "timeout" },
      ],
      coverage_status: "ok",
      dropped_section_ids: [],
      human_review_required: true,
    };
    expect(formatAiRepairHumanReviewMessage(legacy)).toBeNull();
  });

  it("prioritises quota when multiple operational kinds co-occur", () => {
    // Defensive: a run that accumulated quota + billing errors across
    // multiple AI repair attempts surfaces the quota line first (the
    // most-frequently actionable per the issue body ordering).
    const ai: AiRepairStatus = {
      status: "error",
      counts: { ...baseCounts, total: 2, error: 2 },
      api_error_kinds: { quota: 1, billing: 1, auth: 0, other: 0 },
      unsupported_kind_records: [],
      error_records: [
        {
          unit_index: 0,
          source_section_ids: ["03-1"],
          error: "RateLimitError",
          api_error_kind: "quota",
        },
        {
          unit_index: 1,
          source_section_ids: ["03-2"],
          error: "PermissionDeniedError",
          api_error_kind: "billing",
        },
      ],
      coverage_status: "ok",
      dropped_section_ids: [],
      human_review_required: true,
    };
    const msg = formatAiRepairHumanReviewMessage(ai);
    expect(msg).not.toBeNull();
    expect(msg).toContain("API quota");
  });
});
