// SnapshotLineRow — single account line inside the snapshot editor.
//
// Two variants are dispatched by the account's OWN `account.kind` (#213),
// NOT by `category_kind`:
//
//  - `simple`   (Issue #146): a single value input keyed by `account_id`.
//  - `detailed` (Issue #213): N sub-rows, one per security held — each with
//                `quantity`, `unit_price` (both required), a read-only live
//                `value`, and the existing PriceFetchControl. The account's
//                value is the sum across its holdings.
//
// The OLD "priced scalar" variant (one security via account.symbol + scalar
// quantity/unit_price on the line) is SUPERSEDED: migration v16 (#211)
// converted every former-priced account into `kind='detailed'` with one
// holding, so those accounts now flow through the detailed (holdings) path.
//
// #214 turns the detailed variant into the real per-title entry surface: each
// sub-row carries a SecurityPicker (autocomplete over `balance_securities` +
// inline creation), quantity, unit_price (+ price fetch), a read-only computed
// value, a book_cost input, and a live latent-gain figure. The account's value
// is the SUM across its holdings.
//
// We keep this component dumb on purpose: it receives strings from the parent
// (the editor stores raw strings to preserve partial input) and emits new
// strings on every change. Numeric validation happens at save time in
// `useSnapshotEditor.save`.

import { ChangeEvent, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Plus, Trash2, Upload } from "lucide-react";
import type {
  BalanceAccountWithCategory,
  BalanceSecurity,
} from "../../shared/types";
import type { HoldingDraft } from "../../hooks/useSnapshotEditor";
import type { HoldingColumnMapping } from "../../utils/csvAutoDetect";
import PriceFetchControl from "./PriceFetchControl";
import SecurityPicker, { type SecurityPick } from "./SecurityPicker";
import HoldingsCsvImportModal from "./HoldingsCsvImportModal";

interface Props {
  account: BalanceAccountWithCategory;
  disabled?: boolean;
  /** Snapshot date (YYYY-MM-DD) — passed through to PriceFetchControl. */
  snapshotDate?: string;
  /** Simple variant: the scalar value string + its change handler. */
  value: string;
  onChange: (next: string) => void;
  /** Detailed variant: the holdings basket + mutators (#213). */
  holdings?: HoldingDraft[];
  /** Securities catalogue for the SecurityPicker autocomplete (#214). */
  securities?: BalanceSecurity[];
  onAddHolding?: () => void;
  onRemoveHolding?: (rowId: string) => void;
  onHoldingFieldChange?: (
    rowId: string,
    field: keyof Omit<HoldingDraft, "rowId">,
    value: string
  ) => void;
  /** Apply a SecurityPicker selection to a row (symbol + asset_type + name). */
  onHoldingSecurityPick?: (rowId: string, pick: SecurityPick) => void;
  /** Import a batch of holdings from a CSV into this detailed account (#245). */
  onImportHoldings?: (rows: string[][], mapping: HoldingColumnMapping) => void;
}

/**
 * Parse a string like "12.34" or "12,34" into a finite number, or null
 * if invalid / empty. Used by the detailed sub-rows to compute the live
 * `value` preview.
 */
function parseDecimal(raw: string): number | null {
  if (!raw) return null;
  const trimmed = String(raw).trim().replace(",", ".");
  if (!trimmed) return null;
  const n = Number(trimmed);
  return Number.isFinite(n) ? n : null;
}

export default function SnapshotLineRow({
  account,
  value,
  onChange,
  disabled,
  snapshotDate,
  holdings,
  securities,
  onAddHolding,
  onRemoveHolding,
  onHoldingFieldChange,
  onHoldingSecurityPick,
  onImportHoldings,
}: Props) {
  const { t } = useTranslation();
  const isDetailed = account.kind === "detailed";
  const [importOpen, setImportOpen] = useState(false);

  // Account total across the basket (live as the user types).
  const detailedTotal = useMemo(() => {
    if (!isDetailed || !holdings) return null;
    let total = 0;
    for (const h of holdings) {
      const qty = parseDecimal(h.quantity);
      const price = parseDecimal(h.unit_price);
      if (qty !== null && price !== null) total += qty * price;
    }
    return total;
  }, [isDetailed, holdings]);

  if (isDetailed) {
    const rows = holdings ?? [];
    return (
      <div className="py-2 border-b border-[var(--border)] last:border-b-0">
        <div className="flex items-center justify-between gap-2 mb-1">
          <div className="flex items-center gap-2 min-w-0">
            <span className="text-sm font-medium truncate">{account.name}</span>
            <span
              className="text-[10px] uppercase tracking-wide px-1.5 py-0.5 rounded bg-[var(--muted)] text-[var(--muted-foreground)]"
              title={t("balance.snapshot.detailed.badgeHint")}
            >
              {t("balance.snapshot.detailed.badge")}
            </span>
          </div>
          {detailedTotal !== null && (
            <span className="text-sm font-semibold tabular-nums whitespace-nowrap">
              {detailedTotal.toLocaleString(undefined, {
                minimumFractionDigits: 2,
                maximumFractionDigits: 2,
              })}{" "}
              {account.currency}
            </span>
          )}
        </div>

        {rows.length === 0 ? (
          <p className="text-xs text-[var(--muted-foreground)] py-1">
            {t("balance.snapshot.detailed.empty")}
          </p>
        ) : (
          <div className="flex flex-col gap-2">
            {rows.map((h) => (
              <HoldingSubRow
                key={h.rowId}
                holding={h}
                accountName={account.name}
                accountCurrency={account.currency}
                securities={securities ?? []}
                snapshotDate={snapshotDate}
                disabled={disabled}
                onFieldChange={(field, v) =>
                  onHoldingFieldChange?.(h.rowId, field, v)
                }
                onSecurityPick={(pick) =>
                  onHoldingSecurityPick?.(h.rowId, pick)
                }
                onRemove={() => onRemoveHolding?.(h.rowId)}
              />
            ))}
          </div>
        )}

        <div className="mt-2 flex items-center gap-4">
          <button
            type="button"
            onClick={() => onAddHolding?.()}
            disabled={disabled}
            className="inline-flex items-center gap-1 text-xs text-[var(--primary)] hover:underline disabled:opacity-50"
          >
            <Plus size={13} />
            {t("balance.snapshot.detailed.addTitle")}
          </button>
          {onImportHoldings && (
            <button
              type="button"
              onClick={() => setImportOpen(true)}
              disabled={disabled}
              className="inline-flex items-center gap-1 text-xs text-[var(--primary)] hover:underline disabled:opacity-50"
            >
              <Upload size={13} />
              {t("balance.snapshot.detailed.importCsv.button")}
            </button>
          )}
        </div>

        {importOpen && onImportHoldings && (
          <HoldingsCsvImportModal
            accountName={account.name}
            onImport={onImportHoldings}
            onClose={() => setImportOpen(false)}
          />
        )}
      </div>
    );
  }

  // Simple variant — unchanged from #146.
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    onChange(e.target.value);
  };

  return (
    <div className="flex items-center gap-3 py-2 border-b border-[var(--border)] last:border-b-0">
      <div className="flex-1 min-w-0">
        <div className="text-sm font-medium truncate">{account.name}</div>
        {account.symbol && (
          <div className="text-xs text-[var(--muted-foreground)]">
            {account.symbol}
          </div>
        )}
      </div>
      <div className="flex items-center gap-2">
        <input
          type="text"
          inputMode="decimal"
          value={value}
          onChange={handleChange}
          disabled={disabled}
          placeholder={t("balance.snapshot.line.valuePlaceholder")}
          className="w-32 px-3 py-2 rounded-lg border border-[var(--border)] bg-[var(--card)] text-sm text-right focus:outline-none focus:ring-1 focus:ring-[var(--primary)] disabled:opacity-50"
          aria-label={t("balance.snapshot.line.valueLabel", {
            account: account.name,
          })}
        />
        <span className="text-xs text-[var(--muted-foreground)] w-10">
          {account.currency}
        </span>
      </div>
    </div>
  );
}

// -----------------------------------------------------------------------------
// Detailed sub-row — one security position (#214: SecurityPicker + polished
// columns [titre, quantité, cours (+ fetch), valeur, book_cost, gain latent]).
// -----------------------------------------------------------------------------

/** A small labeled field wrapper so each column reads clearly on its own. */
function Field({
  label,
  children,
}: {
  label: string;
  children: React.ReactNode;
}) {
  return (
    <label className="flex flex-col gap-0.5">
      <span className="text-[10px] uppercase tracking-wide text-[var(--muted-foreground)]">
        {label}
      </span>
      {children}
    </label>
  );
}

function HoldingSubRow({
  holding,
  accountName,
  accountCurrency,
  securities,
  snapshotDate,
  disabled,
  onFieldChange,
  onSecurityPick,
  onRemove,
}: {
  holding: HoldingDraft;
  accountName: string;
  accountCurrency: string;
  securities: BalanceSecurity[];
  snapshotDate?: string;
  disabled?: boolean;
  onFieldChange: (field: keyof Omit<HoldingDraft, "rowId">, value: string) => void;
  onSecurityPick: (pick: SecurityPick) => void;
  onRemove: () => void;
}) {
  const { t } = useTranslation();
  const computedValue = useMemo(() => {
    const qty = parseDecimal(holding.quantity);
    const price = parseDecimal(holding.unit_price);
    if (qty === null || price === null) return null;
    return qty * price;
  }, [holding.quantity, holding.unit_price]);

  // Live latent gain = value − book_cost. N/A when value can't be computed or
  // book_cost is empty / zero (consistent with computeUnrealizedGain's guard,
  // which treats a 0 book_cost as "no meaningful gain figure" for display).
  const latentGain = useMemo(() => {
    if (computedValue === null) return null;
    const bookCost = parseDecimal(holding.book_cost);
    if (bookCost === null || bookCost === 0) return null;
    return computedValue - bookCost;
  }, [computedValue, holding.book_cost]);

  const label = holding.symbol || accountName;
  const fmt2 = (n: number) =>
    n.toLocaleString(undefined, {
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
    });

  return (
    <div className="flex flex-wrap items-end gap-2 pl-2 py-1 border-l-2 border-[var(--border)]">
      {/* Titre — SecurityPicker (autocomplete + inline create) */}
      <Field label={t("balance.snapshot.detailed.col.title")}>
        <SecurityPicker
          securities={securities}
          value={holding.symbol}
          assetType={holding.asset_type}
          onSelect={onSecurityPick}
          disabled={disabled}
          ariaLabel={t("balance.snapshot.detailed.symbolLabel")}
        />
      </Field>

      {/* Quantité */}
      <Field label={t("balance.snapshot.detailed.col.quantity")}>
        <input
          type="text"
          inputMode="decimal"
          value={holding.quantity}
          onChange={(e) => onFieldChange("quantity", e.target.value)}
          disabled={disabled}
          placeholder={t("balance.snapshot.priced.quantityPlaceholder")}
          className="w-20 px-2 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--card)] text-sm text-right focus:outline-none focus:ring-1 focus:ring-[var(--primary)] disabled:opacity-50"
          aria-label={t("balance.snapshot.priced.quantityLabel", {
            account: label,
          })}
        />
      </Field>

      {/* Cours (unit price) */}
      <Field label={t("balance.snapshot.detailed.col.unitPrice")}>
        <input
          type="text"
          inputMode="decimal"
          value={holding.unit_price}
          onChange={(e) => onFieldChange("unit_price", e.target.value)}
          disabled={disabled}
          placeholder={t("balance.snapshot.priced.unitPricePlaceholder")}
          className="w-24 px-2 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--card)] text-sm text-right focus:outline-none focus:ring-1 focus:ring-[var(--primary)] disabled:opacity-50"
          aria-label={t("balance.snapshot.priced.unitPriceLabel", {
            account: label,
          })}
        />
      </Field>

      {/* Valeur (computed, read-only) */}
      <Field label={t("balance.snapshot.detailed.col.value")}>
        <input
          type="text"
          value={computedValue === null ? "" : computedValue.toFixed(2)}
          readOnly
          disabled
          placeholder={t("balance.snapshot.priced.computedValuePlaceholder")}
          className="w-28 px-2 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--muted)]/40 text-sm text-right text-[var(--muted-foreground)] cursor-not-allowed"
          aria-label={t("balance.snapshot.priced.computedValueLabel", {
            account: label,
          })}
          aria-readonly="true"
        />
      </Field>

      {/* Book cost (cost basis) */}
      <Field label={t("balance.snapshot.detailed.col.bookCost")}>
        <input
          type="text"
          inputMode="decimal"
          value={holding.book_cost}
          onChange={(e) => onFieldChange("book_cost", e.target.value)}
          disabled={disabled}
          placeholder={t("balance.snapshot.detailed.bookCostPlaceholder")}
          className="w-24 px-2 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--card)] text-sm text-right focus:outline-none focus:ring-1 focus:ring-[var(--primary)] disabled:opacity-50"
          aria-label={t("balance.snapshot.detailed.bookCostLabel", {
            account: label,
          })}
        />
      </Field>

      {/* Gain latent (value − book_cost), live, read-only */}
      <Field label={t("balance.snapshot.detailed.col.latentGain")}>
        <span
          className={`inline-flex items-center justify-end w-24 px-2 py-1.5 text-sm text-right tabular-nums ${
            latentGain === null
              ? "text-[var(--muted-foreground)]"
              : latentGain >= 0
                ? "text-[var(--positive)]"
                : "text-[var(--negative)]"
          }`}
          aria-label={t("balance.snapshot.detailed.latentGainLabel", {
            account: label,
          })}
        >
          {latentGain === null
            ? t("balance.snapshot.detailed.latentGainNA")
            : `${latentGain >= 0 ? "+" : ""}${fmt2(latentGain)}`}
        </span>
      </Field>

      <span className="text-xs text-[var(--muted-foreground)] pb-2">
        {accountCurrency}
      </span>

      {holding.symbol && (
        <div className="pb-0.5">
          <PriceFetchControl
            symbol={holding.symbol}
            date={snapshotDate ?? ""}
            categoryKind={"priced"}
            assetType={holding.asset_type}
            onPriceFetched={(price) =>
              onFieldChange("unit_price", String(price))
            }
          />
        </div>
      )}

      <button
        type="button"
        onClick={onRemove}
        disabled={disabled}
        className="p-1 mb-1 rounded text-[var(--muted-foreground)] hover:text-[var(--negative)] hover:bg-[var(--negative)]/10 disabled:opacity-50"
        title={t("balance.snapshot.detailed.removeTitle")}
        aria-label={t("balance.snapshot.detailed.removeTitle")}
      >
        <Trash2 size={14} />
      </button>
    </div>
  );
}
