import { useId } from "react";
import type { ComparisonTableProps } from "./schema";

/** 列ごとに述語を満たす値の数を集計する（scoreColumns/booleanRowCounts共通の集計形） */
const tallyColumns = (
  rows: { values: (boolean | string)[] }[],
  columnCount: number,
  predicate: (v: boolean | string) => boolean,
): number[] => {
  const counts = Array(columnCount).fill(0);
  for (const row of rows) {
    row.values.forEach((v, i) => {
      if (predicate(v)) counts[i] += 1;
    });
  }
  return counts;
};

/** 列ごとに true の数を集計する（string 値は集計対象外） */
export const scoreColumns = (
  rows: { values: (boolean | string)[] }[],
  columnCount: number,
): number[] => tallyColumns(rows, columnCount, (v) => v === true);

/**
 * 最多スコアの列 index を返す。全列0点なら強調なし(null)、同点は先頭列を採用
 * （DESIGN.md: 大胆さは1画面1箇所）。
 */
export const bestColumnIndex = (scores: number[]): number | null => {
  const max = Math.max(...scores);
  if (max <= 0) return null;
  return scores.indexOf(max);
};

/** highlight（ベスト列）専用グラデーション（DESIGN.md: indigo→violet 限定・1件のみ） */
const HIGHLIGHT_GRADIENT = "bg-linear-to-br from-tertiary to-accent-violet";

/**
 * 列ごとにboolean値を持つ行数（集計フッターの分母）。string値の行
 * （初期費用/月額料金など）は分母から除外する — rows.length をそのまま
 * 使うと文字列行が混ざったデータで分母が過大になる。
 */
export const booleanRowCounts = (
  rows: { values: (boolean | string)[] }[],
  columnCount: number,
): number[] => tallyColumns(rows, columnCount, (v) => typeof v === "boolean");

/** 分母0件は「–」にフォールバックする（0/0という無意味な表記を避ける） */
export const formatTally = (score: number, total: number): string =>
  total === 0 ? "–" : `${score}/${total}`;

/** desktop/mobileと評価根拠の対応を共有する安定した座標code */
export const columnCode = (columnIndex: number): string =>
  `C${String(columnIndex + 1).padStart(2, "0")}`;

export const criterionCode = (rowIndex: number): string =>
  `E${String(rowIndex + 1).padStart(2, "0")}`;

export type EvidenceKind = "supporting" | "counter" | "context";

/** 推奨列の値がboolean集計へどう関与するかを表示用に分類する */
export const evidenceKind = (value: boolean | string): EvidenceKind =>
  value === true ? "supporting" : value === false ? "counter" : "context";

/** mobileは推奨候補を先に読み、残りは元の入力順を保つ */
export const mobileColumnOrder = (
  columnCount: number,
  bestColumn: number | null,
): number[] => {
  const inputOrder = Array.from({ length: columnCount }, (_, index) => index);
  if (bestColumn === null) return inputOrder;
  return [
    bestColumn,
    ...inputOrder.filter((columnIndex) => columnIndex !== bestColumn),
  ];
};

const evidenceLabel = (kind: EvidenceKind): string =>
  kind === "supporting" ? "加点" : kind === "counter" ? "非加点" : "参考";

const ValueMark = ({ value }: { value: boolean | string }) =>
  typeof value === "boolean" ? (
    value ? (
      <span
        aria-label="対応"
        data-comparison-mark="positive"
        className="text-success"
      >
        ○
      </span>
    ) : (
      <span
        aria-label="非対応"
        data-comparison-mark="negative"
        className="text-error"
      >
        ×
      </span>
    )
  ) : (
    <span>{value}</span>
  );

export default function ComparisonTable({
  title,
  subtitle,
  columns,
  rows,
}: ComparisonTableProps) {
  const autoId = useId();
  const uid =
    autoId.replace(
      // 捨てる操作は単射でないので、schema を通る別々の値が同じ id になる
      // （`sales:a` と `sales/a` がどちらも `salesa`）。同じ文書へ 2 つ置くと
      // id と radio の name が衝突して、片方の操作がもう片方に効く。
      // 逃がし文字（`_`）自身も逃がすので、この変換は単射。
      /[^a-zA-Z0-9-]/g,
      (c) => `_${c.charCodeAt(0).toString(16)}_`,
    ) || "comparison-table";
  const scores = scoreColumns(rows, columns.length);
  const best = bestColumnIndex(scores);
  const totals = booleanRowCounts(rows, columns.length);
  const notes = rows.filter((row) => row.note);
  const mobileOrder = mobileColumnOrder(columns.length, best);
  const interactionCss: string = rows
    .map((_, rowIndex) => {
      const code = criterionCode(rowIndex);
      return `
[data-comparison-root="${uid}"]:has([data-comparison-hotspot="${code}"]:is(:hover,:focus)) [data-comparison-criterion-label="${code}"] {
  background-color: color-mix(in srgb, var(--color-primary) 7%, var(--color-surface));
  box-shadow: inset var(--spacing) 0 0 var(--color-primary);
  color: var(--color-primary);
}
[data-comparison-root="${uid}"]:has([data-comparison-hotspot="${code}"]:is(:hover,:focus)) [data-comparison-surface="${code}"] {
  box-shadow: inset var(--spacing) 0 0 var(--color-primary);
}`;
    })
    .join("\n");

  return (
    <section
      data-comparison-root={uid}
      className="mx-auto max-w-slide bg-surface p-5 font-sans text-on-surface sm:p-12"
    >
      <style>{interactionCss}</style>
      <header className="mb-7">
        <h1 className="text-h1">{title}</h1>
        {subtitle && (
          <p className="mt-2 text-body-sm text-secondary">{subtitle}</p>
        )}
      </header>

      <div data-comparison-view="desktop" className="hidden sm:block">
        <table className="w-full table-fixed border-collapse border border-outline text-body-sm">
          <thead>
            <tr>
              <th className="w-1/3 border border-outline bg-primary p-3 text-left text-on-primary">
                <span className="block font-mono text-label opacity-70">
                  EVIDENCE
                </span>
                <span className="mt-1 block text-label">評価軸</span>
              </th>
              {columns.map((col, ci) => (
                <th
                  key={ci}
                  data-comparison-column={columnCode(ci)}
                  data-comparison-spine={ci === best ? "header" : undefined}
                  data-priority={ci === best ? "true" : undefined}
                  className={`border border-outline p-3 text-center ${
                    ci === best
                      ? `${HIGHLIGHT_GRADIENT} text-on-tertiary`
                      : "bg-primary text-on-primary"
                  }`}
                >
                  <span className="block font-mono text-label opacity-70">
                    {columnCode(ci)}
                  </span>
                  <span className="mt-1 block text-label">{col}</span>
                  {ci === best && (
                    <span className="mt-2 block border-t border-on-tertiary/30 pt-2 text-label">
                      おすすめ · {formatTally(scores[ci], totals[ci])}
                    </span>
                  )}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {rows.map((row, ri) => (
              <tr
                key={ri}
                data-comparison-row={criterionCode(ri)}
                className={
                  ri % 2 === 1 ? "bg-surface-subtle" : "bg-surface"
                }
              >
                <th
                  scope="row"
                  data-comparison-criterion-label={criterionCode(ri)}
                  className="border border-outline p-3 text-left font-normal text-secondary transition-colors duration-150"
                >
                  <span className="block font-mono text-label text-tertiary">
                    {criterionCode(ri)}
                  </span>
                  <span className="mt-1 block">{row.criterion}</span>
                </th>
                {row.values.map((value, ci) => {
                  const kind = evidenceKind(value);
                  const isBest = ci === best;
                  return (
                    <td
                      key={ci}
                      data-comparison-column={columnCode(ci)}
                      data-comparison-spine={isBest ? "body" : undefined}
                      data-comparison-surface={
                        isBest ? criterionCode(ri) : undefined
                      }
                      className={`border border-outline p-3 text-center ${
                        isBest
                          ? "relative bg-tertiary-container/30 before:absolute before:inset-y-0 before:left-0 before:w-1 before:bg-tertiary before:content-['']"
                          : ""
                      }`}
                    >
                      {isBest ? (
                        <div
                          tabIndex={0}
                          data-comparison-evidence={kind}
                          data-comparison-hotspot={criterionCode(ri)}
                          className="flex min-h-12 items-center justify-between gap-2 text-left outline-none transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tertiary"
                        >
                          <span className="text-body">
                            <ValueMark value={value} />
                          </span>
                          <span
                            className={`text-label ${
                              kind === "supporting"
                                ? "text-success"
                                : kind === "counter"
                                  ? "text-error"
                                  : "text-secondary"
                            }`}
                          >
                            {evidenceLabel(kind)}
                          </span>
                        </div>
                      ) : (
                        <ValueMark value={value} />
                      )}
                    </td>
                  );
                })}
              </tr>
            ))}
          </tbody>
          <tfoot>
            <tr className="border-t-2 border-t-outline">
              <th
                scope="row"
                className="border border-outline bg-surface p-3 text-left text-label text-secondary"
              >
                ○の合計
              </th>
              {columns.map((_, ci) => (
                <td
                  key={ci}
                  data-comparison-column={columnCode(ci)}
                  data-comparison-spine={ci === best ? "footer" : undefined}
                  className={`border border-outline p-3 text-center text-label ${
                    ci === best
                      ? "relative bg-tertiary-container/30 text-tertiary before:absolute before:inset-y-0 before:left-0 before:w-1 before:bg-tertiary before:content-['']"
                      : "text-secondary"
                  }`}
                >
                  {formatTally(scores[ci], totals[ci])}
                </td>
              ))}
            </tr>
          </tfoot>
        </table>
      </div>

      <div data-comparison-view="mobile" className="sm:hidden">
        {best !== null && (
          <section
            data-comparison-mobile-summary="true"
            data-comparison-column={columnCode(best)}
            className={`${HIGHLIGHT_GRADIENT} mb-5 p-5 text-on-tertiary`}
          >
            <p className="font-mono text-label opacity-70">
              RECOMMENDATION · {columnCode(best)}
            </p>
            <div className="mt-2 flex items-end justify-between gap-4">
              <h2 className="text-h2">{columns[best]}</h2>
              <p className="font-mono text-body">
                ○ {formatTally(scores[best], totals[best])}
              </p>
            </div>
          </section>
        )}

        <div className="border-y border-outline">
          {rows.map((row, rowIndex) => {
            const rowCode = criterionCode(rowIndex);
            return (
              <article
                key={rowIndex}
                data-comparison-mobile-criterion={rowCode}
                className="border-b border-outline py-4 last:border-b-0"
              >
                <header className="mb-3 flex items-baseline gap-3">
                  <span className="font-mono text-label text-tertiary">
                    {rowCode}
                  </span>
                  <h3 className="text-label">{row.criterion}</h3>
                </header>
                <div className="grid gap-2">
                  {mobileOrder.map((columnIndex) => {
                    const value = row.values[columnIndex];
                    const kind = evidenceKind(value);
                    const isBest = columnIndex === best;
                    return (
                      <div
                        key={columnIndex}
                        data-comparison-mobile-option={columnIndex}
                        data-comparison-column={columnCode(columnIndex)}
                        data-recommended={isBest ? "true" : undefined}
                        className={`grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 px-3 py-2 ${
                          isBest
                            ? "relative bg-tertiary-container/30 before:absolute before:inset-y-0 before:left-0 before:w-1 before:bg-tertiary before:content-['']"
                            : "bg-surface-subtle"
                        }`}
                      >
                        <div className="min-w-0">
                          <p className="font-mono text-label text-tertiary">
                            {columnCode(columnIndex)}
                            {isBest && (
                              <span className="ml-2 text-secondary">
                                推奨列
                              </span>
                            )}
                          </p>
                          <p className="mt-1 truncate text-body-sm">
                            {columns[columnIndex]}
                          </p>
                        </div>
                        <div className="text-right">
                          <p className="text-body">
                            <ValueMark value={value} />
                          </p>
                          {isBest && (
                            <p
                              data-comparison-evidence={kind}
                              className={`mt-1 text-label ${
                                kind === "supporting"
                                  ? "text-success"
                                  : kind === "counter"
                                    ? "text-error"
                                    : "text-secondary"
                              }`}
                            >
                              {evidenceLabel(kind)}
                            </p>
                          )}
                        </div>
                      </div>
                    );
                  })}
                </div>
              </article>
            );
          })}
        </div>

        <div className="mt-4 flex items-center justify-between border-b border-outline pb-4 text-label">
          <span className="text-secondary">○の合計</span>
          <span className="font-mono text-tertiary">
            {best === null
              ? "推奨なし"
              : `${columns[best]} · ${formatTally(scores[best], totals[best])}`}
          </span>
        </div>
      </div>

      <p className="mt-3 text-label text-secondary">
        ○の数が最多の列を自動判定。加点・非加点・参考値を同じ推奨spineで確認
      </p>

      {notes.length > 0 && (
        <details
          data-inline-disclosure="true"
          className="group mt-6 rounded-lg border border-outline bg-surface-subtle"
        >
          <summary className="flex cursor-pointer list-none items-center px-4 py-3 text-label text-secondary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tertiary">
            <span>補足を確認</span>
            <span className="ml-auto font-mono">{notes.length}件</span>
            <span
              aria-hidden="true"
              className="ml-2 text-tertiary transition-transform group-open:rotate-45"
            >
              ＋
            </span>
          </summary>
          <div className="grid gap-3 border-t border-outline p-4 sm:grid-cols-2">
            {notes.map((row, index) => (
              <article
                key={index}
                data-note-item="true"
                className="rounded-md bg-surface p-4"
              >
                <h3 className="text-label">{row.criterion}</h3>
                <p className="mt-2 text-body-sm">{row.note}</p>
              </article>
            ))}
          </div>
        </details>
      )}
    </section>
  );
}
