import { useId } from "react";
import type { ValueActivity, ValueChainMapProps } from "./schema";

const round = (value: number) => Number(value.toFixed(2));

// 2 つの識別子を `-` で繋ぐと単射にならない —— ("a-b","c") と ("a","b-c") が
// 同じ鍵になり、React の key が重複する。区切りに使う文字を各辺で逃がしてから繋ぐ。
const joinIds = (...parts: (string | number)[]) =>
  parts.map((p) => String(p).replace(/[_-]/g, (c) => `_${c.charCodeAt(0).toString(16)}_`)).join("-");

export function activityGeometry(activities: ValueActivity[]) {
  const total = activities.reduce((sum, item) => sum + item.cost, 0);
  let cumulative = 0;
  return activities.map((item) => {
    const startPercent = round((cumulative / total) * 100);
    cumulative += item.cost;
    const endPercent = round((cumulative / total) * 100);
    return {
      ...item,
      startPercent,
      widthPercent: round((item.cost / total) * 100),
      endPercent,
      heightPercent: (item.value / 5) * 100,
    };
  });
}

export function rankValueGaps(activities: ValueActivity[]) {
  return activities
    .map((item, index) => ({
      ...item,
      gap: item.cost * (6 - item.value),
      inputIndex: index,
    }))
    .sort((a, b) => b.gap - a.gap || a.inputIndex - b.inputIndex);
}

export default function ValueChainMap({
  title,
  subtitle,
  costUnit,
  supportActivities,
  primaryActivities,
  instanceId,
}: ValueChainMapProps) {
  // 既定を固定文字列にすると、id を指定せず 2 つ置いたときに radio の name と
  // SVG marker の id を共有してしまう。他の型と同じく useId() に寄せる。
  const autoId = useId();
  const uid = (instanceId ?? autoId).replace(
      // 逃がし文字（`_`）自身も逃がすので単射。捨てる形にすると `a:b` と `a/b` が
      // 同じ id になる。
      /[^a-zA-Z0-9-]/g,
      (c: string) => `_${c.charCodeAt(0).toString(16)}_`,
    ) || "value-chain";
  const geometry = activityGeometry(primaryActivities).map((activity) => ({
    ...activity,
    startX: round(80 + activity.startPercent * 8.6),
    endX: round(80 + activity.endPercent * 8.6),
    halfHeight: 14 + activity.value * 13,
  }));
  const ranked = rankValueGaps(primaryActivities);
  const priority = ranked[0];
  const notes = primaryActivities.filter((item) => item.note);
  const markerId = `${uid}-value-arrow`;
  const centerY = 232;

  return (
    <section
      className="asset group/value overflow-hidden rounded-xl border border-outline bg-surface p-5 text-primary shadow-card sm:p-8"
      data-layout="value-chain-map"
    >
      <header className="border-b border-outline pb-5">
        <p className="font-mono text-label text-tertiary">VALUE RIVERBED</p>
        <h1 className="mt-2 text-h1 text-balance">{title}</h1>
        {subtitle && <p className="mt-2 text-body text-secondary">{subtitle}</p>}
      </header>

      <div className="mt-5 flex flex-wrap justify-between gap-3">
        <div className="inline-flex rounded-full border border-outline bg-surface-subtle p-1 text-label">
          <label className="rounded-full px-3 py-1.5 has-[:checked]:bg-primary has-[:checked]:text-on-primary">
            <input className="value-view-chain sr-only" type="radio" name={`${uid}-view`} defaultChecked />価値連鎖
          </label>
          <label className="rounded-full px-3 py-1.5 has-[:checked]:bg-primary has-[:checked]:text-on-primary">
            <input className="value-view-gap sr-only" type="radio" name={`${uid}-view`} />改善余地
          </label>
        </div>
        <span className="font-mono text-label text-secondary">LENGTH=COST · THICKNESS=VALUE · RAIL=SUPPORT</span>
      </div>

      <div
        className="mt-5 grid gap-2 border-l-4 border-tertiary bg-gradient-to-r from-tertiary/20 to-transparent px-4 py-3 sm:grid-cols-[auto_1fr_auto] sm:items-center"
        data-value-chain-default-priority="true"
      >
        <span className="font-mono text-label text-tertiary">IMPROVE FIRST</span>
        <strong>{priority.label}</strong>
        <span className="font-mono text-h3">GAP {priority.gap}</span>
      </div>

      <div data-value-chain-view="chain" className="mt-6 group-has-[.value-view-gap:checked]/value:hidden">
        <svg
          className="hidden h-80 w-full sm:block"
          viewBox="0 0 1000 360"
          role="img"
          aria-label="コストを長さ、価値を太さとして連続表示するバリューチェーン"
          data-primary-geometry="value-chain-map"
          data-geometry-channels="cost-length value-thickness sequence-position support-rail gap-emphasis"
        >
          <defs>
            <marker id={markerId} viewBox="0 0 10 10" refX="8" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto">
              <path d="M 0 0 L 10 5 L 0 10 z" className="fill-surface" />
            </marker>
          </defs>

          {supportActivities.map((item, index) => {
            const y = 35 + index * 28;
            return (
              <g key={item}>
                <line x1="80" x2="940" y1={y} y2={y} className="stroke-outline" strokeWidth="6" strokeLinecap="round" />
                <circle cx="80" cy={y} r="6" className="fill-primary" />
                <text x="94" y={y + 4} className="fill-secondary font-mono text-label">S{index + 1} · {item}</text>
              </g>
            );
          })}

          <text x="80" y="116" className="fill-secondary font-mono text-label">PRIMARY VALUE STREAM</text>
          {geometry.map((activity, index) => {
            const isPriority = activity.id === priority.id;
            const centerX = (activity.startX + activity.endX) / 2;
            const previous = geometry[index - 1];
            const next = geometry[index + 1];
            const startHalf = previous
              ? (previous.halfHeight + activity.halfHeight) / 2
              : activity.halfHeight;
            const endHalf = next
              ? (activity.halfHeight + next.halfHeight) / 2
              : activity.halfHeight;
            const labelAbove = index % 2 === 0;
            const labelY = labelAbove ? centerY - activity.halfHeight - 44 : 335;
            const edgeY = labelAbove ? centerY - activity.halfHeight : centerY + activity.halfHeight;
            const ribbonPoints = `${activity.startX},${centerY - startHalf} ${centerX},${centerY - activity.halfHeight} ${activity.endX},${centerY - endHalf} ${activity.endX},${centerY + endHalf} ${centerX},${centerY + activity.halfHeight} ${activity.startX},${centerY + startHalf}`;
            const haloPoints = `${activity.startX - 6},${centerY - startHalf - 9} ${centerX},${centerY - activity.halfHeight - 9} ${activity.endX + 6},${centerY - endHalf - 9} ${activity.endX + 6},${centerY + endHalf + 9} ${centerX},${centerY + activity.halfHeight + 9} ${activity.startX - 6},${centerY + startHalf + 9}`;
            return (
              <g
                key={activity.id}
                tabIndex={0}
                data-value-chain-activity={activity.id}
                data-primary-record={activity.id}
                data-geometry-a={activity.cost}
                data-geometry-b={activity.value}
                data-primary-emphasis={isPriority ? "true" : undefined}
              >
                {isPriority && (
                  <polygon
                    points={haloPoints}
                    className="fill-tertiary/15"
                  />
                )}
                <polygon
                  points={ribbonPoints}
                  className={isPriority ? "fill-tertiary" : "fill-primary"}
                />
                <line x1={centerX} x2={centerX} y1={edgeY} y2={labelAbove ? labelY + 8 : labelY - 20} className="stroke-outline" vectorEffect="non-scaling-stroke" />
                <text data-primary-label={activity.id} x={centerX} y={labelY} textAnchor="middle" className="fill-primary text-label font-semibold">{index + 1} · {activity.label}</text>
                <text x={centerX} y={labelY + 17} textAnchor="middle" className="fill-secondary font-mono text-label">{activity.cost}{costUnit} · V{activity.value}</text>
              </g>
            );
          })}

          {geometry.slice(0, -1).map((activity, index) => {
            const next = geometry[index + 1];
            const startX = (activity.startX + activity.endX) / 2 + 12;
            const endX = (next.startX + next.endX) / 2 - 12;
            return (
              <path
                key={joinIds(activity.id, next.id)}
                d={`M ${startX} ${centerY} H ${endX}`}
                fill="none"
                className="stroke-surface"
                strokeWidth="3"
                vectorEffect="non-scaling-stroke"
                markerEnd={`url(#${markerId})`}
                data-primary-edge={joinIds(activity.id, next.id)}
              />
            );
          })}
        </svg>

        <div className="relative space-y-3 pl-7 sm:hidden">
          <span className="absolute bottom-2 left-2 top-2 w-px bg-outline" aria-hidden="true" />
          {primaryActivities.map((activity, index) => (
            <article key={activity.id} data-value-chain-activity={activity.id} data-value-chain-mobile-activity={activity.id} className="relative grid grid-cols-[3rem_1fr_auto] gap-3 border-b border-outline py-3 pl-3">
              <span className={`absolute -left-6 top-4 h-3 w-3 rounded-full ${activity.id === priority.id ? "bg-tertiary ring-4 ring-tertiary/20" : "bg-primary"}`} aria-hidden="true" />
              <span className="font-mono text-label text-tertiary">P{index + 1}</span>
              <div><strong>{activity.label}</strong><div className="mt-2 h-1.5 bg-outline"><span className="block h-full bg-primary" style={{ width: `${geometry[index].widthPercent}%` }} /></div></div>
              <span className="font-mono text-label">{activity.cost}{costUnit} · V{activity.value}</span>
            </article>
          ))}
        </div>
      </div>

      <div data-value-chain-view="gap-ledger" className="mt-6 hidden group-has-[.value-view-gap:checked]/value:block">
        <div className="divide-y divide-outline border-y border-outline">
          {ranked.map((activity, index) => (
            <article key={activity.id} data-value-chain-activity={activity.id} className={`grid grid-cols-[3rem_1fr_auto] items-center gap-3 py-3 ${index === 0 ? "border-l-4 border-tertiary pl-3" : "pl-4"}`}>
              <span className="font-mono text-label text-secondary">{String(index + 1).padStart(2, "0")}</span>
              <div><strong>{activity.label}</strong><p className="text-label text-secondary">{activity.cost}{costUnit} × 価値不足{6 - activity.value}</p></div>
              <span className="font-mono text-h3">{activity.gap}</span>
            </article>
          ))}
        </div>
      </div>

      {notes.length > 0 && (
        <details data-inline-disclosure className="mt-6 rounded-lg border border-outline bg-surface-subtle p-4">
          <summary className="cursor-pointer font-semibold">補足を確認（{notes.length}件）</summary>
          {notes.map((item) => <p key={item.id} data-note-item="true" className="mt-3 text-body text-secondary"><strong className="text-primary">{item.label}:</strong> {item.note}</p>)}
        </details>
      )}
    </section>
  );
}
