"use client";

/**
 * FailedView — Failed state（轉檔失敗、錯誤訊息翻譯 + 建議解決方法）
 *
 * Phase 0.8 conversion (見 .autoflow/03-design/wireframes/wireframe-conversion.md §3.5 + §8)
 *
 * 對齊：
 *   - flow-conversion.md §5.6 failed → 顯示翻譯訊息 + 建議 + 重新開始
 *   - feature-converter-integration.md §F5（錯誤碼對照表 + unknown fallback）
 *   - api-conversion.md §6（error_code 在 backend 直接傳入，非 i18n key 形式）
 *
 * 範圍（F-T8）：
 *   - 失敗 hero（紅色 ✗ + 「轉檔失敗」title），role="alert" + aria-live="assertive"
 *   - 任務摘要：source filename → target chip（含「失敗」字尾）
 *   - 錯誤訊息卡片：翻譯後的訊息 + error code（給 ops）+ Job ID 前 8 字元
 *   - 建議解決方法清單（最多 3 項；找不到對應 key 走 unknown fallback）
 *   - 「重新開始」主按鈕 → store.reset() 切回 idle
 *   - i18n key fallback：未知 code 一律走 conversion.error.unknown.*
 *
 * 不做：
 *   - 「回模型庫」按鈕（wireframe §8 有列出但任務未要求；可由 sidebar 切換）
 *   - 「複製任務 ID」按鈕（wireframe §8 有 nice-to-have；F-T9 視情況補）
 *
 * a11y：
 *   - 失敗 hero `role="alert"` + `aria-live="assertive"`（重要訊息、即時播報）
 *   - error code 用 `<code>` 標記（pre-formatted、SR 不會逐字朗讀）
 *   - 「重新開始」按鈕有明確 aria-label
 *   - Dark mode 對齊既有 destructive token
 */

import { AlertCircleIcon, RefreshCwIcon } from "lucide-react";
import { useMemo } from "react";

import { Button } from "@/components/ui/button";
import type { TranslateFn } from "@/lib/i18n/context";
import { useT } from "@/lib/i18n/context";
import { useConversionStore } from "@/stores/conversion-store";
import type { ConversionJob } from "@/types/conversion";

/** Job ID 前 N 字元 — wireframe §3.5 顯示「abcd1234（前 8 字元）」 */
const JOB_ID_DISPLAY_PREFIX = 8;
/** Suggestions 上限 — i18n key 設計支援 suggestion1/2/3 */
const MAX_SUGGESTIONS = 3;
/** Unknown fallback 用的 i18n code key 段 */
const UNKNOWN_CODE_KEY = "unknown";

/* -------------------------------------------------------------------------- */
/* Helpers — 安全的 i18n lookup（找不到 key 不要 throw / 不要顯示 raw key）      */
/* -------------------------------------------------------------------------- */

/**
 * 安全取得錯誤訊息 — 找不到對應 i18n key 時，自動 fallback 到 `conversion.error.unknown.message`。
 *
 * useT 的 t(key) 在找不到時回傳 key 本身（context.tsx §78–92）；我們用「翻譯結果 === key」
 * 偵測 miss，然後 fallback。對齊 flow-conversion.md §5.6 的虛擬碼。
 *
 * @param t       - useT 取得的翻譯函式
 * @param code    - converter 回傳的 error code（如 `QUANTIZATION_FAILED` / 任意未知字串）
 * @returns 翻譯後的文字；fallback 失敗（連 unknown key 都不存在）時回空字串
 */
export function getErrorMessage(t: TranslateFn, code: string): string {
  const key = `conversion.error.${code}.message`;
  const translated = t(key);
  if (translated !== key) return translated;

  // fallback：unknown code
  const fallbackKey = `conversion.error.${UNKNOWN_CODE_KEY}.message`;
  const fallback = t(fallbackKey);
  if (fallback !== fallbackKey) return fallback;

  // fallback 也找不到（不應發生；i18n 字典必含 unknown.message）
  return "";
}

/**
 * 安全取得建議解決方法清單（最多 3 條）。
 *
 * 規則：
 *   1. 先嘗試 `conversion.error.<code>.suggestion1/2/3`
 *   2. 任一條找不到就停（不繼續往下找；3 條共用同一個 namespace）
 *   3. 全部找不到 → fallback 到 `conversion.error.unknown.suggestion1/2/3`
 *
 * 為什麼不混合（找到 1 條後 fallback 補 2、3）：
 *   - 各 code 的 suggestion 彼此語意不同（QUANTIZATION_FAILED 的「移除 Custom Op」對 unknown 沒意義）
 *   - 若某 code 只有 suggestion1，那它就只該顯示 1 條，補 unknown 的反而誤導
 */
export function getErrorSuggestions(t: TranslateFn, code: string): string[] {
  const suggestions = collectSuggestions(t, code);
  if (suggestions.length > 0) return suggestions;
  return collectSuggestions(t, UNKNOWN_CODE_KEY);
}

/** 從特定 code 連續抓 suggestion1..N，遇到第一個 miss 就停 */
function collectSuggestions(t: TranslateFn, code: string): string[] {
  const out: string[] = [];
  for (let i = 1; i <= MAX_SUGGESTIONS; i++) {
    const key = `conversion.error.${code}.suggestion${i}`;
    const translated = t(key);
    if (translated === key) break;
    out.push(translated);
  }
  return out;
}

/* -------------------------------------------------------------------------- */
/* Component                                                                  */
/* -------------------------------------------------------------------------- */

export function FailedView() {
  const t = useT();
  const job = useConversionStore((s) => s.job);
  const reset = useConversionStore((s) => s.reset);

  // 防呆：page.tsx 已 routing；理論上 failed state 必有 job（store 切 failed 同時設 job）
  if (!job) return null;

  return <FailedViewInner job={job} reset={reset} t={t} />;
}

interface FailedViewInnerProps {
  job: ConversionJob;
  reset: () => void;
  t: TranslateFn;
}

function FailedViewInner({ job, reset, t }: FailedViewInnerProps) {
  /* ---- 衍生資料 ---- */
  // error code：優先用 job.error.code；極端情況 backend 漏給時 fallback unknown
  const errorCode = job.error?.code ?? UNKNOWN_CODE_KEY;
  const errorMessage = useMemo(
    () => getErrorMessage(t, errorCode),
    [t, errorCode],
  );
  const suggestions = useMemo(
    () => getErrorSuggestions(t, errorCode),
    [t, errorCode],
  );

  const sourceFilename = job.source_filename || "";
  const targetChip = job.target_chip;
  const shortJobId = job.job_id.slice(0, JOB_ID_DISPLAY_PREFIX);

  /* ---- handler ---- */
  const handleRestart = () => {
    // store.reset() 會清掉 polling / upload controller；切回 idle 後 page.tsx 自動換 IdleForm
    reset();
  };

  return (
    <div data-testid="conversion-failed" className="space-y-6">
      {/* 失敗 hero — role="alert" + aria-live="assertive"（重要錯誤訊息應主動播報） */}
      <div
        role="alert"
        aria-live="assertive"
        aria-label={t("conversion.failed.aria.alert")}
        data-testid="failed-hero"
        className="border-destructive/40 bg-destructive/5 dark:bg-destructive/10 flex items-start gap-3 rounded-xl border p-6"
      >
        <AlertCircleIcon
          aria-hidden="true"
          className="text-destructive size-6 shrink-0"
        />
        <div className="space-y-1">
          <h2 className="text-lg font-semibold">
            {t("conversion.failed.heading")}
          </h2>
          {/* 翻譯後的錯誤訊息（unknown fallback 已內建於 helper） */}
          <p
            data-testid="failed-message"
            className="text-foreground text-sm"
          >
            {errorMessage}
          </p>
        </div>
      </div>

      {/* 任務摘要 + 錯誤碼 + Job ID */}
      <div
        data-testid="failed-summary"
        className="bg-card space-y-3 rounded-xl border p-6"
      >
        {/* 檔名 → chip（含「失敗」字尾） */}
        <div className="flex flex-wrap items-center gap-x-2 gap-y-1 text-sm">
          {sourceFilename ? (
            <span className="font-mono" data-testid="failed-source-filename">
              {sourceFilename}
            </span>
          ) : null}
          <span aria-hidden="true" className="text-muted-foreground">
            →
          </span>
          <span
            className="bg-muted text-muted-foreground rounded px-2 py-0.5 text-xs font-medium"
            data-testid="failed-target-chip"
          >
            {targetChip}
          </span>
          <span className="text-muted-foreground text-xs">
            {t("conversion.failed.summary.failedSuffix")}
          </span>
        </div>

        {/* 錯誤代碼 + Job ID — dl 結構，給 SR 標籤明確 */}
        <dl className="grid gap-3 text-sm sm:grid-cols-2">
          <div className="space-y-0.5">
            <dt className="text-muted-foreground text-xs">
              {t("conversion.failed.errorCode")}
            </dt>
            <dd>
              <code
                data-testid="failed-error-code"
                className="bg-muted text-foreground rounded px-1.5 py-0.5 font-mono text-xs"
              >
                {errorCode}
              </code>
            </dd>
          </div>
          <div className="space-y-0.5">
            <dt className="text-muted-foreground text-xs">
              {t("conversion.failed.jobIdLabel")}
            </dt>
            <dd
              data-testid="failed-job-id"
              className="font-mono text-xs"
              // 完整 ID 給 ops 複製，但顯示只到前 8 字（wireframe §3.5）
              title={job.job_id}
            >
              {shortJobId}
            </dd>
          </div>
        </dl>
      </div>

      {/* 建議解決方法 */}
      {suggestions.length > 0 ? (
        <div
          data-testid="failed-suggestions"
          className="space-y-2"
        >
          <p className="text-sm font-medium">
            {t("conversion.failed.suggestionsTitle")}
          </p>
          <ul
            className="text-muted-foreground list-disc space-y-1 pl-5 text-sm"
            data-testid="failed-suggestions-list"
          >
            {suggestions.map((s, idx) => (
              <li key={idx} data-testid={`failed-suggestion-${idx}`}>
                {s}
              </li>
            ))}
          </ul>
        </div>
      ) : null}

      {/* 主動作：重新開始（wireframe §3.5：在卡片外面下方對齊右側） */}
      <div className="flex justify-end border-t pt-4">
        <Button
          type="button"
          variant="default"
          size="lg"
          onClick={handleRestart}
          aria-label={t("conversion.failed.aria.retry")}
          data-testid="failed-restart"
          className="gap-2"
        >
          <RefreshCwIcon aria-hidden="true" className="size-4" />
          {t("conversion.failed.retry")}
        </Button>
      </div>
    </div>
  );
}
