import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import CategoryCombobox from "../shared/CategoryCombobox";
import type { Category } from "../../shared/types";
import type {
  MappingRow as MappingRowType,
  ConfidenceBadge,
} from "../../services/categoryMappingService";

interface MappingRowProps {
  row: MappingRowType;
  /** When true, the row is highlighted (its preview panel is open). */
  isSelected: boolean;
  /** Callback fired when the row is clicked — opens the preview panel. */
  onSelect: (v2CategoryId: number) => void;
  /**
   * Called with the new v1 target id + name when the user picks a target via
   * the inline type-ahead combobox. Editable on EVERY row (resolved or not),
   * so a user can override an auto-detected target as well as resolve a
   * "needs review" one.
   */
  onResolve: (v2CategoryId: number, v1TargetId: number, v1TargetName: string) => void;
  /** Number of transactions currently attached to this v2 category. */
  transactionCount: number;
  /**
   * v1 LEAF catalogue adapted to the `Category` shape the combobox expects.
   * Computed ONCE by StepSimulate and shared across rows (perf). Leaves only —
   * a transaction is never mapped to a grouping bucket.
   */
  targetCategories: Category[];
  /**
   * Resolves a target id (possibly a non-leaf parent absent from the leaves-only
   * `targetCategories`) to a display `Category`. Used to keep a low-confidence
   * parent default like "Divertissement" (#1710) visible on its row without
   * offering parents as new picks.
   */
  resolveTarget: (id: number) => Category | null;
}

function badgeClass(confidence: ConfidenceBadge): string {
  switch (confidence) {
    case "high":
      return "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300";
    case "medium":
      return "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300";
    case "low":
      return "bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300";
    case "none":
      return "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300";
  }
}

export default function MappingRow({
  row,
  isSelected,
  onSelect,
  onResolve,
  transactionCount,
  targetCategories,
  resolveTarget,
}: MappingRowProps) {
  const { t } = useTranslation();

  // The picker lists leaves only. If this row's current target is a non-leaf
  // parent (a low-confidence default like "Divertissement" #1710), it is absent
  // from that list and would blank the combobox input, so re-inject it for this
  // row only — shown as the current value, never offered as a new pick.
  const comboboxCategories = useMemo(() => {
    const id = row.v1TargetId;
    if (id == null || targetCategories.some((c) => c.id === id)) {
      return targetCategories;
    }
    const current = resolveTarget(id);
    return current ? [...targetCategories, current] : targetCategories;
  }, [targetCategories, row.v1TargetId, resolveTarget]);

  const badgeLabel = t(
    `categoriesSeed.migration.simulate.confidence.${row.confidence}`,
  );
  const reasonLabel = t(
    `categoriesSeed.migration.simulate.reason.${row.reason}`,
  );

  const isUnresolved = row.v1TargetId === null || row.v1TargetId === undefined;

  // Fired when the user picks a v1 leaf in the type-ahead combobox. The
  // combobox only emits ids that exist in `targetCategories` (never null with
  // our config), but we guard defensively. Resolving a "none" row bumps its
  // confidence to "medium" via the reducer; editing an already-resolved row
  // keeps its confidence unchanged (see RESOLVE_ROW).
  const handleTargetChange = (v1TargetId: number | null) => {
    if (v1TargetId === null) return;
    const target = targetCategories.find((c) => c.id === v1TargetId);
    if (!target) return;
    const name = target.i18n_key
      ? t(target.i18n_key, { defaultValue: target.name })
      : target.name;
    onResolve(row.v2CategoryId, v1TargetId, name);
  };

  const rowClass =
    "grid grid-cols-12 gap-2 items-center px-3 py-2 rounded-md border text-sm cursor-pointer transition-colors " +
    (isSelected
      ? "bg-[var(--primary)]/10 border-[var(--primary)]/40"
      : "bg-[var(--card)] border-[var(--border)] hover:border-[var(--primary)]/30 hover:bg-[var(--muted)]");

  const targetDisplayName = isUnresolved
    ? null
    : row.v1TargetName;

  return (
    <div
      className={rowClass}
      onClick={() => onSelect(row.v2CategoryId)}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => {
        if (e.key === "Enter" || e.key === " ") {
          e.preventDefault();
          onSelect(row.v2CategoryId);
        }
      }}
      aria-label={`${row.v2CategoryName} → ${targetDisplayName ?? t("categoriesSeed.migration.simulate.needsReview")}`}
    >
      {/* v2 category name + tx count */}
      <div className="col-span-4 flex items-center gap-2 min-w-0">
        <span className="truncate font-medium text-[var(--foreground)]">
          {row.v2CategoryName}
        </span>
        <span className="shrink-0 text-xs text-[var(--muted-foreground)]">
          {t("categoriesSeed.migration.simulate.txCount", {
            count: transactionCount,
          })}
        </span>
      </div>

      {/* Confidence badge + reason */}
      <div className="col-span-3 flex items-center gap-2">
        <span
          className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${badgeClass(
            row.confidence,
          )}`}
          title={row.notes ?? undefined}
        >
          {badgeLabel}
        </span>
        <span className="text-xs text-[var(--muted-foreground)] truncate">
          {reasonLabel}
        </span>
      </div>

      {/* v1 target — editable type-ahead combobox on EVERY row. Stop click +
          keydown from bubbling so interacting with the picker (typing spaces,
          Enter to select) never triggers the row's select/preview handler. */}
      <div
        className="col-span-5 flex items-center justify-end min-w-0"
        onClick={(e) => e.stopPropagation()}
        onKeyDown={(e) => e.stopPropagation()}
      >
        <div className="w-full max-w-[16rem]">
          <CategoryCombobox
            categories={comboboxCategories}
            value={row.v1TargetId ?? null}
            onChange={handleTargetChange}
            compact
            placeholder={t("categoriesSeed.migration.simulate.chooseTarget")}
            ariaLabel={t("categoriesSeed.migration.simulate.editTargetAria", {
              category: row.v2CategoryName,
            })}
          />
        </div>
      </div>
    </div>
  );
}
