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

/** weight に比例した区間幅。0以下は可視性を保つ最小値へクランプする。 */
export const stepWidths = (steps: { weight: number }[]): number[] =>
  steps.map((step) => Math.max(0.1, step.weight));

/** 最も weight が大きい工程のindex。同値は先頭を採用する。 */
export const bottleneckIndex = (steps: { weight: number }[]): number => {
  let maxIndex = 0;
  for (let index = 1; index < steps.length; index++) {
    if (steps[index].weight > steps[maxIndex].weight) maxIndex = index;
  }
  return maxIndex;
};

/**
 * 各工程の中央位置(%)。クランプ済み区間幅の累積から求める。
 * ラベル、connector、操作対象を同じ工程断面へ揃えるために使う。
 */
export const stepCenters = (steps: { weight: number }[]): number[] => {
  const widths = stepWidths(steps);
  const total = widths.reduce((sum, width) => sum + width, 0);
  let elapsed = 0;
  return widths.map((width) => {
    const start = elapsed;
    elapsed += width;
    return ((start + elapsed) / 2 / total) * 100;
  });
};

/**
 * 各hand-off時点の生weight累計。クランプ値は描画だけに使い、
 * 表示する日数へ混ぜない。
 */
export const cumulativeDays = (steps: { weight: number }[]): number[] => {
  const result: number[] = [];
  let elapsed = 0;
  for (let index = 0; index < steps.length - 1; index++) {
    elapsed += steps[index].weight;
    result.push(elapsed);
  }
  return result;
};

export type ProcessLaneSegment = {
  originalIndex: number;
  startPct: number;
  endPct: number;
  widthPct: number;
  centerPct: number;
  cumulativeDays: number;
};

/**
 * 独立カードの幅ではなく、一本のprocess lane上の連続区間へ工程を写す。
 * endPctは必ず次工程のstartPctになり、最終工程は100%で終わる。
 */
export function processLaneGeometry(
  steps: readonly { weight: number }[],
): ProcessLaneSegment[] {
  const widths = stepWidths([...steps]);
  const totalWidth = widths.reduce((sum, width) => sum + width, 0);
  let widthElapsed = 0;
  let daysElapsed = 0;

  return widths.map((width, originalIndex) => {
    const startPct = (widthElapsed / totalWidth) * 100;
    widthElapsed += width;
    daysElapsed += steps[originalIndex].weight;
    const endPct = (widthElapsed / totalWidth) * 100;
    return {
      originalIndex,
      startPct,
      endPct,
      widthPct: endPct - startPct,
      centerPct: (startPct + endPct) / 2,
      cumulativeDays: daysElapsed,
    };
  });
}

const HIGHLIGHT_GRADIENT = "bg-linear-to-br from-tertiary to-accent-violet";
const stagger = (index: number) =>
  `calc(var(--stagger-step) * ${index})`;

const formatDays = (value: number) =>
  new Intl.NumberFormat("ja-JP", { maximumFractionDigits: 1 }).format(value);

export default function ProcessFlow({
  title,
  subtitle,
  steps,
}: ProcessFlowProps) {
  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)}_`,
    ) || "process";
  const geometry = processLaneGeometry(steps);
  const bottleneck = steps.length > 0 ? bottleneckIndex(steps) : -1;
  const totalDays = steps.reduce((sum, step) => sum + step.weight, 0);
  const notes = steps.filter((step) => step.note);

  const interactionStyles = `
[data-process-instance="${uid}"] [data-process-segment] {
  transition: background-color 140ms ease-out, color 140ms ease-out;
}
[data-process-instance="${uid}"] [data-process-record],
[data-process-instance="${uid}"] [data-process-connector],
[data-process-instance="${uid}"] [data-process-boundary] {
  transition: color 140ms ease-out, border-color 140ms ease-out, opacity 140ms ease-out;
}
[data-process-instance="${uid}"] [data-process-hotspot]:focus-visible {
  outline: none;
}
${geometry
  .map(
    (segment) => `
[data-process-instance="${uid}"]:has([data-process-hotspot~="${segment.originalIndex}"]:is(:hover, :focus)) [data-process-segment][data-step-index="${segment.originalIndex}"]:not([data-priority]) {
  background-color: var(--color-tertiary-container);
  color: var(--color-on-tertiary-container);
}
[data-process-instance="${uid}"]:has([data-process-hotspot~="${segment.originalIndex}"]:is(:hover, :focus)) [data-process-record][data-step-index="${segment.originalIndex}"] {
  color: var(--color-tertiary);
}
[data-process-instance="${uid}"]:has([data-process-hotspot~="${segment.originalIndex}"]:is(:hover, :focus)) [data-process-connector][data-step-index="${segment.originalIndex}"],
[data-process-instance="${uid}"]:has([data-process-hotspot~="${segment.originalIndex}"]:is(:hover, :focus)) [data-process-boundary][data-step-index="${segment.originalIndex}"] {
  border-color: var(--color-tertiary);
  opacity: 1;
}`,
  )
  .join("")}
`;

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

      {steps.length === 0 ? (
        <p className="text-body-sm text-secondary">工程が未設定</p>
      ) : (
        <>
          {/* desktop: 所要日数を横軸にした一本の連続lane */}
          <div
            data-process-lane="desktop"
            className="relative hidden pt-16 pb-16 sm:block"
          >
            <div className="mb-3 flex items-center justify-between font-mono text-label text-secondary">
              <span>START 0日</span>
              <span>TOTAL {formatDays(totalDays)}日</span>
            </div>

            <div className="relative flex h-20 shadow-card">
              {steps.map((step, index) => {
                const segment = geometry[index];
                const isBottleneck = index === bottleneck;
                const labelAbove = index % 2 === 0;
                const edgeLabel =
                  index === 0
                    ? "left-0 text-left"
                    : index === steps.length - 1
                      ? "right-0 text-right"
                      : "left-1/2 -translate-x-1/2 text-center";
                const round =
                  index === 0
                    ? "rounded-l-lg border-l"
                    : index === steps.length - 1
                      ? "rounded-r-lg"
                      : "";

                return (
                  <div
                    key={index}
                    data-process-segment="true"
                    data-process-hotspot={index}
                    data-step-index={index}
                    data-priority={isBottleneck ? "true" : undefined}
                    tabIndex={0}
                    role="group"
                    aria-label={`STEP ${index + 1} ${step.label}、${formatDays(step.weight)}日、累計${formatDays(segment.cumulativeDays)}日${isBottleneck ? "、最も所要日数が長い工程" : ""}`}
                    className={`relative min-w-0 animate-rise cursor-default border-y border-r border-outline ${
                      isBottleneck
                        ? `${HIGHLIGHT_GRADIENT} text-on-tertiary`
                        : "bg-neutral text-on-neutral"
                    } ${round}`}
                    style={{
                      flexGrow: segment.widthPct,
                      flexBasis: 0,
                      animationDelay: stagger(index),
                    }}
                  >
                    <div className="pointer-events-none absolute inset-0 flex items-center justify-center overflow-hidden px-1">
                      <span className="font-mono text-label opacity-70">
                        {String(index + 1).padStart(2, "0")}
                      </span>
                    </div>

                    <span
                      data-process-connector="true"
                      data-step-index={index}
                      aria-hidden="true"
                      className={`absolute left-1/2 h-8 border-l border-dashed border-outline opacity-70 ${
                        labelAbove ? "bottom-full" : "top-full"
                      }`}
                    />
                    <div
                      data-process-record="true"
                      data-step-index={index}
                      className={`absolute w-36 text-on-surface ${edgeLabel} ${
                        labelAbove ? "bottom-full mb-9" : "top-full mt-9"
                      }`}
                    >
                      <p className="font-mono text-label text-secondary">
                        STEP {String(index + 1).padStart(2, "0")}
                      </p>
                      <p className="mt-1 text-body-sm">{step.label}</p>
                      <p className="mt-1 font-mono text-label">
                        {formatDays(step.weight)}日
                        {isBottleneck && (
                          <span className="ml-2 text-tertiary">最大</span>
                        )}
                      </p>
                    </div>

                    {index < steps.length - 1 && (
                      <span
                        data-process-boundary="true"
                        data-step-index={index}
                        data-cumulative-days={segment.cumulativeDays}
                        aria-label={`累計${formatDays(segment.cumulativeDays)}日`}
                        className="absolute inset-y-0 right-0 border-r border-surface/80 opacity-80"
                      >
                        <span className="absolute right-2 bottom-1 whitespace-nowrap font-mono text-label opacity-75">
                          {formatDays(segment.cumulativeDays)}日
                        </span>
                      </span>
                    )}
                  </div>
                );
              })}
            </div>
            <p className="mt-3 text-right text-label text-secondary">
              区間長 = 所要日数 ・ 境界値 = 開始からの累計日数
            </p>
          </div>

          {/* mobile: 同じ連続区間を縦軸へ回し、横overflowを避ける */}
          <div data-process-lane="mobile" className="relative h-128 sm:hidden">
            <div className="absolute inset-y-0 left-0 w-14 shadow-card">
              {steps.map((step, index) => {
                const segment = geometry[index];
                const isBottleneck = index === bottleneck;
                const round =
                  index === 0
                    ? "rounded-t-lg border-t"
                    : index === steps.length - 1
                      ? "rounded-b-lg"
                      : "";
                return (
                  <div
                    key={index}
                    data-process-segment="true"
                    data-process-hotspot={index}
                    data-step-index={index}
                    data-priority={isBottleneck ? "true" : undefined}
                    tabIndex={0}
                    role="group"
                    aria-label={`STEP ${index + 1} ${step.label}、${formatDays(step.weight)}日、累計${formatDays(segment.cumulativeDays)}日${isBottleneck ? "、最も所要日数が長い工程" : ""}`}
                    className={`absolute inset-x-0 animate-rise cursor-default border-x border-b border-outline ${
                      isBottleneck
                        ? `${HIGHLIGHT_GRADIENT} text-on-tertiary`
                        : "bg-neutral text-on-neutral"
                    } ${round}`}
                    style={{
                      top: `${segment.startPct}%`,
                      height: `${segment.widthPct}%`,
                      animationDelay: stagger(index),
                    }}
                  >
                    <span className="pointer-events-none absolute inset-0 flex items-center justify-center overflow-hidden font-mono text-label opacity-70">
                      {String(index + 1).padStart(2, "0")}
                    </span>
                    <span
                      data-process-connector="true"
                      data-step-index={index}
                      aria-hidden="true"
                      className="absolute top-1/2 left-full w-4 border-t border-dashed border-outline opacity-70"
                    />
                    <div
                      data-process-record="true"
                      data-step-index={index}
                      className="absolute top-1/2 left-full ml-5 w-64 -translate-y-1/2 text-on-surface"
                    >
                      <p className="font-mono text-label text-secondary">
                        STEP {String(index + 1).padStart(2, "0")}
                      </p>
                      <p className="text-body-sm">{step.label}</p>
                      <p className="font-mono text-label">
                        {formatDays(step.weight)}日 ・ 累計
                        {formatDays(segment.cumulativeDays)}日
                        {isBottleneck && (
                          <span className="ml-2 text-tertiary">最大</span>
                        )}
                      </p>
                    </div>

                    {index < steps.length - 1 && (
                      <span
                        data-process-boundary="true"
                        data-step-index={index}
                        data-cumulative-days={segment.cumulativeDays}
                        aria-label={`累計${formatDays(segment.cumulativeDays)}日`}
                        className="absolute inset-x-0 bottom-0 border-b border-surface/80 opacity-80"
                      />
                    )}
                  </div>
                );
              })}
            </div>
          </div>

          <p className="mt-4 text-label text-secondary">
            最も所要日数の長い工程「{steps[bottleneck].label}
            」を連続lane上で自動強調
          </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((step, index) => (
              <article
                key={index}
                data-note-item="true"
                className="rounded-md bg-surface p-4"
              >
                <h3 className="text-label">{step.label}</h3>
                <p className="mt-1 font-mono text-label text-secondary">
                  {formatDays(step.weight)}日
                  {step.owner ? ` ・ 担当 ${step.owner}` : ""}
                </p>
                <p className="mt-2 text-body-sm">{step.note}</p>
              </article>
            ))}
          </div>
        </details>
      )}
    </section>
  );
}
