import { useId } from "react";
import type {
  BusinessCapability,
  BusinessCapabilityMapProps,
  CapabilityDomain,
} from "./schema";

export type FlatCapability = BusinessCapability & {
  domainName: string;
  domainIndex: number;
  capabilityIndex: number;
  originalIndex: number;
  identity: string;
  coordinate: string;
  gap: number;
  widthPercent: number;
};

export function capabilityGap(
  capability: Pick<BusinessCapability, "maturity" | "strategicPriority">,
) {
  return capability.strategicPriority * (6 - capability.maturity);
}

export function capabilityIdentity(domainIndex: number, capabilityIndex: number) {
  return `d${domainIndex}-c${capabilityIndex}`;
}

export function capabilityCoordinate(
  domainIndex: number,
  capabilityIndex: number,
) {
  return `D${String(domainIndex + 1).padStart(2, "0")} · C${String(
    capabilityIndex + 1,
  ).padStart(2, "0")}`;
}

export function capabilityWidthPercent(
  capability: Pick<BusinessCapability, "effort">,
  capabilities: readonly Pick<BusinessCapability, "effort">[],
) {
  const total = capabilities.reduce((sum, item) => sum + item.effort, 0);
  return total > 0 ? (capability.effort / total) * 100 : 0;
}

export function flattenCapabilities(
  domains: readonly CapabilityDomain[],
): FlatCapability[] {
  let originalIndex = 0;
  return domains.flatMap((domain, domainIndex) =>
    domain.capabilities.map((capability, capabilityIndex) => ({
      ...capability,
      domainName: domain.name,
      domainIndex,
      capabilityIndex,
      originalIndex: originalIndex++,
      identity: capabilityIdentity(domainIndex, capabilityIndex),
      coordinate: capabilityCoordinate(domainIndex, capabilityIndex),
      gap: capabilityGap(capability),
      widthPercent: capabilityWidthPercent(capability, domain.capabilities),
    })),
  );
}

export function rankedCapabilities(domains: readonly CapabilityDomain[]) {
  return flattenCapabilities(domains).sort(
    (a, b) => b.gap - a.gap || a.originalIndex - b.originalIndex,
  );
}

export function priorityCapability(domains: readonly CapabilityDomain[]) {
  return rankedCapabilities(domains)[0] ?? null;
}

const MATURITY_CLASSES = [
  "bg-surface-subtle",
  "bg-tertiary/10",
  "bg-tertiary/20",
  "bg-tertiary/30",
  "bg-tertiary/40",
] as const;

const PRIORITY_CLASSES = [
  "border-l-outline",
  "border-l-primary/30",
  "border-l-primary/50",
  "border-l-primary/70",
  "border-l-primary",
] as const;

function metricLabel(capability: BusinessCapability, effortUnit: string) {
  return `${capability.effort}${effortUnit} · M${capability.maturity} · P${capability.strategicPriority}`;
}

function capabilitySurfaceClass(capability: FlatCapability, priority: boolean) {
  if (priority) {
    return "border-tertiary bg-gradient-to-br from-tertiary to-accent-violet text-on-primary shadow-card";
  }
  return `border-outline ${MATURITY_CLASSES[capability.maturity - 1]} ${PRIORITY_CLASSES[capability.strategicPriority - 1]} text-on-surface`;
}

export default function BusinessCapabilityMap({
  title,
  subtitle,
  effortUnit,
  domains,
  instanceId,
}: BusinessCapabilityMapProps) {
  const autoId = useId();
  const uid =
    (instanceId ?? autoId).replace(
      // 捨てる操作は単射でないので、schema を通る別々の値が同じ id になる
      // （`sales:a` と `sales/a` がどちらも `salesa`）。同じ文書へ 2 つ置くと
      // id と radio の name が衝突して、片方の操作がもう片方に効く。
      // 逃がし文字（`_`）自身も逃がすので、この変換は単射。
      /[^a-zA-Z0-9-]/g,
      (c) => `_${c.charCodeAt(0).toString(16)}_`,
    ) || "capability-map";
  const radioName = `capability-view-${uid}`;
  const capabilities = flattenCapabilities(domains);
  const ranked = rankedCapabilities(domains);
  const priority = priorityCapability(domains);
  const notes = capabilities.filter((capability) => capability.note);
  const interactionCss = capabilities
    .map(
      (capability) => `
[data-capability-root="${uid}"]:has([data-capability-hotspot="${capability.identity}"]:is(:hover,:focus-visible)) [data-capability-surface="${capability.identity}"] {
  border-color: var(--color-primary);
  box-shadow: inset .2rem 0 0 var(--color-primary);
  filter: none;
}
[data-capability-root="${uid}"]:has([data-capability-hotspot="${capability.identity}"]:is(:hover,:focus-visible)) [data-capability-default-priority="true"]:not([data-capability-surface="${capability.identity}"]) {
  filter: saturate(.3) brightness(1.08);
}`,
    )
    .join("\n");

  return (
    <section
      data-capability-root={uid}
      className="group/capability mx-auto max-w-slide bg-surface p-12 font-sans text-on-surface"
    >
      <style>{interactionCss}</style>
      <header className="mb-6 flex flex-wrap items-end justify-between gap-4">
        <div className="max-w-4xl">
          <p className="mb-2 font-mono text-label text-tertiary">
            BUSINESS CAPABILITY PORTFOLIO
          </p>
          <h1 className="text-h1">{title}</h1>
          {subtitle && (
            <p className="mt-2 text-body-sm text-secondary">{subtitle}</p>
          )}
        </div>

        <div className="flex shrink-0 rounded-sm border border-outline bg-surface-subtle p-1 shadow-card">
          <input
            id={`${uid}-map`}
            type="radio"
            name={radioName}
            defaultChecked
            className="capability-view-map sr-only"
          />
          <label
            htmlFor={`${uid}-map`}
            className="cursor-pointer rounded-sm px-4 py-1.5 text-label text-secondary group-has-[.capability-view-map:checked]/capability:bg-primary group-has-[.capability-view-map:checked]/capability:text-on-primary group-has-[.capability-view-map:focus-visible]/capability:ring-2 group-has-[.capability-view-map:focus-visible]/capability:ring-tertiary"
          >
            能力マップ
          </label>
          <input
            id={`${uid}-gaps`}
            type="radio"
            name={radioName}
            className="capability-view-gaps sr-only"
          />
          <label
            htmlFor={`${uid}-gaps`}
            className="cursor-pointer rounded-sm px-4 py-1.5 text-label text-secondary group-has-[.capability-view-gaps:checked]/capability:bg-primary group-has-[.capability-view-gaps:checked]/capability:text-on-primary group-has-[.capability-view-gaps:focus-visible]/capability:ring-2 group-has-[.capability-view-gaps:focus-visible]/capability:ring-tertiary"
          >
            投資ギャップ
          </label>
        </div>
      </header>

      {priority && (
        <div className="mb-6 grid gap-3 border-y border-outline py-4 sm:grid-cols-[auto_minmax(0,1fr)_auto] sm:items-center sm:gap-6">
          <div>
            <p className="font-mono text-label text-tertiary">PRIORITY GAP</p>
            <p className="mt-0.5 font-mono text-h3">{priority.coordinate}</p>
          </div>
          <div className="min-w-0">
            <p className="text-label text-secondary">{priority.domainName}</p>
            <p className="font-semibold">{priority.name}</p>
          </div>
          <div className="sm:text-right">
            <p className="font-mono text-h2">{priority.gap}</p>
            <p className="font-mono text-label text-secondary">
              P{priority.strategicPriority} × (6 − M{priority.maturity})
            </p>
          </div>
        </div>
      )}

      <div className="block group-has-[.capability-view-gaps:checked]/capability:hidden">
        <div
          data-capability-view="map-desktop"
          className="hidden space-y-3 sm:block"
        >
          {domains.map((domain, domainIndex) => {
            const domainCapabilities = capabilities.filter(
              (capability) => capability.domainIndex === domainIndex,
            );
            const domainEffort = domain.capabilities.reduce(
              (sum, capability) => sum + capability.effort,
              0,
            );
            return (
              <div
                key={domain.name}
                data-capability-domain={domainIndex}
                className="grid grid-cols-[10rem_minmax(0,1fr)] border-y border-outline"
              >
                <div className="flex flex-col justify-between border-r border-outline bg-primary px-4 py-4 text-on-primary">
                  <div>
                    <p className="font-mono text-label text-on-primary/60">
                      D{String(domainIndex + 1).padStart(2, "0")}
                    </p>
                    <h2 className="mt-1 text-h3">{domain.name}</h2>
                  </div>
                  <p className="mt-4 font-mono text-label text-on-primary/70">
                    {domainEffort} {effortUnit}
                  </p>
                </div>
                <div className="flex min-w-0 gap-1 bg-outline p-1">
                  {domainCapabilities.map((capability) => {
                    const isPriority = capability.identity === priority?.identity;
                    return (
                      <article
                        key={capability.identity}
                        tabIndex={0}
                        data-capability-id={capability.identity}
                        data-capability-hotspot={capability.identity}
                        data-capability-surface={capability.identity}
                        data-capability-default-priority={
                          isPriority ? "true" : undefined
                        }
                        data-capability-gap={capability.gap}
                        data-capability-width={capability.widthPercent.toFixed(2)}
                        className={`min-w-0 border border-l-4 p-3 outline-none transition-[border-color,box-shadow,filter] duration-fast focus-visible:ring-2 focus-visible:ring-tertiary ${capabilitySurfaceClass(capability, isPriority)}`}
                        style={{
                          flexBasis: `${capability.widthPercent}%`,
                          flexGrow: capability.widthPercent,
                        }}
                        aria-label={`${domain.name}、${capability.name}、${metricLabel(capability, effortUnit)}`}
                      >
                        <p className="font-mono text-label opacity-70">
                          {capability.coordinate}
                        </p>
                        <p className="mt-2 break-words font-semibold leading-snug">
                          {capability.name}
                        </p>
                        <p className="mt-3 font-mono text-label opacity-80">
                          {metricLabel(capability, effortUnit)}
                        </p>
                      </article>
                    );
                  })}
                </div>
              </div>
            );
          })}
        </div>

        <div
          data-capability-view="map-mobile"
          className="space-y-5 sm:hidden"
        >
          {domains.map((domain, domainIndex) => (
            <section key={domain.name} data-capability-mobile-domain={domainIndex}>
              <div className="mb-2 flex items-baseline justify-between gap-3 border-b border-outline pb-2">
                <h2 className="text-h3">{domain.name}</h2>
                <span className="font-mono text-label text-secondary">
                  D{String(domainIndex + 1).padStart(2, "0")}
                </span>
              </div>
              <div className="space-y-1">
                {capabilities
                  .filter((capability) => capability.domainIndex === domainIndex)
                  .map((capability) => {
                    const isPriority = capability.identity === priority?.identity;
                    return (
                      <article
                        key={capability.identity}
                        tabIndex={0}
                        data-capability-id={capability.identity}
                        data-capability-hotspot={capability.identity}
                        data-capability-surface={capability.identity}
                        data-capability-default-priority={
                          isPriority ? "true" : undefined
                        }
                        data-capability-gap={capability.gap}
                        className={`grid min-w-0 grid-cols-[minmax(0,1fr)_auto] gap-3 border border-l-4 p-3 outline-none transition-[border-color,box-shadow,filter] duration-fast focus-visible:ring-2 focus-visible:ring-tertiary ${capabilitySurfaceClass(capability, isPriority)}`}
                      >
                        <div className="min-w-0">
                          <p className="break-words font-semibold">
                            {capability.name}
                          </p>
                          <div className="mt-2 h-1.5 overflow-hidden rounded-full bg-outline/60">
                            <span
                              className="block h-full bg-current opacity-50"
                              style={{ width: `${capability.widthPercent}%` }}
                            />
                          </div>
                        </div>
                        <div className="text-right font-mono text-label">
                          <p>{capability.effort}{effortUnit}</p>
                          <p className="mt-1 opacity-70">
                            M{capability.maturity} · P{capability.strategicPriority} · G{capability.gap}
                          </p>
                        </div>
                      </article>
                    );
                  })}
              </div>
            </section>
          ))}
        </div>
      </div>

      <div
        data-capability-view="gap-ledger"
        className="hidden group-has-[.capability-view-gaps:checked]/capability:block"
      >
        <div className="mb-3 grid grid-cols-[3rem_minmax(0,1fr)_auto] gap-3 border-b border-outline pb-2 font-mono text-label text-secondary">
          <span>RANK</span>
          <span>CAPABILITY / EVIDENCE</span>
          <span>GAP</span>
        </div>
        <div className="divide-y divide-outline border-b border-outline">
          {ranked.map((capability, index) => {
            const isPriority = capability.identity === priority?.identity;
            return (
              <article
                key={capability.identity}
                tabIndex={0}
                data-capability-id={capability.identity}
                data-capability-hotspot={capability.identity}
                data-capability-surface={capability.identity}
                data-capability-default-priority={isPriority ? "true" : undefined}
                data-capability-gap={capability.gap}
                className={`grid min-w-0 grid-cols-[3rem_minmax(0,1fr)_auto] items-center gap-3 border-l-4 px-2 py-3 outline-none transition-[border-color,box-shadow,filter] duration-fast focus-visible:ring-2 focus-visible:ring-tertiary ${isPriority ? "border-l-tertiary bg-gradient-to-r from-tertiary/20 to-transparent" : "border-l-transparent"}`}
              >
                <span className="font-mono text-label text-secondary">
                  {String(index + 1).padStart(2, "0")}
                </span>
                <div className="min-w-0">
                  <div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
                    <p className="font-semibold">{capability.name}</p>
                    <p className="text-label text-secondary">
                      {capability.domainName} · {capability.coordinate}
                    </p>
                  </div>
                  <div className="mt-2 h-1.5 overflow-hidden rounded-full bg-outline/60">
                    <span
                      className={isPriority ? "block h-full bg-gradient-to-r from-tertiary to-accent-violet" : "block h-full bg-primary"}
                      style={{ width: `${(capability.gap / 25) * 100}%` }}
                    />
                  </div>
                  <p className="mt-2 font-mono text-label text-secondary">
                    {capability.effort}{effortUnit} · 成熟度 M{capability.maturity} · 優先度 P{capability.strategicPriority}
                  </p>
                </div>
                <div className="text-right">
                  <p className="font-mono text-h3">{capability.gap}</p>
                  <p className="font-mono text-label text-secondary">/ 25</p>
                </div>
              </article>
            );
          })}
        </div>
      </div>

      <div className="mt-6 flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-outline pt-4 text-label text-secondary">
        <span className="font-mono">M = 成熟度</span>
        <span className="font-mono">P = 戦略優先度</span>
        <span className="font-mono">G = P × (6 − M)</span>
        <span>区画幅 = ドメイン内の{effortUnit}構成比</span>
      </div>

      {notes.length > 0 && (
        <details
          data-inline-disclosure="true"
          className="mt-6 border border-outline bg-surface-subtle"
        >
          <summary className="cursor-pointer px-4 py-3 font-semibold marker:text-tertiary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tertiary">
            補足を確認（{notes.length}件）
          </summary>
          <div className="grid gap-3 border-t border-outline p-4 sm:grid-cols-2">
            {notes.map((capability) => (
              <article
                key={capability.identity}
                data-note-item="true"
                className="border-l-2 border-tertiary pl-3"
              >
                <p className="text-label text-secondary">
                  {capability.domainName} · {capability.coordinate}
                </p>
                <p className="mt-1 font-semibold">{capability.name}</p>
                <p className="mt-1 text-body-sm text-secondary">
                  {capability.note}
                </p>
              </article>
            ))}
          </div>
        </details>
      )}
    </section>
  );
}
