/**
 * SkuGrid — renders the cartesian-product SKU table for variant pricing.
 *
 * Receives axes + current skus from the parent (controlled). Calls onChange
 * whenever a cell is edited. Parent is responsible for calling the cartesian
 * product expansion whenever axes change.
 */
'use client';

import * as React from 'react';
import * as Dialog from '@radix-ui/react-dialog';
import { useT } from '@/lib/i18n/react';
import { Table } from '@/components/ui/primitives/Table';
import { Button } from '@/components/ui/primitives/Button';
import { Input } from '@/components/ui/primitives/Input';
import { NumberInput } from '@/components/ui/primitives/NumberInput';
import { Switch } from '@/components/ui/primitives/Switch';
import { DealImageManager, type DealImageEntryUI } from '@/components/ui/domain/DealImageManager';
import { QtyTierEditor } from './QtyTierEditor';
import { discountedPriceStr } from './discount';
import type { AddDealFormValues } from './useAddDeal';

export type SkuRow = NonNullable<AddDealFormValues['variantsSkus']>[number];
export type AxisSpec = NonNullable<AddDealFormValues['variantsAxes']>[number];

// ─── Image-set helpers ────────────────────────────────────────────────────────

/** Mirrors skuImageSet schema: one entry per SKU, keyed by optionValueCodes. */
export type SkuImageSetEntry = { optionValueCodes: string[]; images: DealImageEntryUI[] };

function findSkuImages(
  skuSets: SkuImageSetEntry[] | undefined,
  optionValueCodes: string[],
): DealImageEntryUI[] {
  const key = optionValueCodes.join('|');
  return skuSets?.find((s) => s.optionValueCodes.join('|') === key)?.images ?? [];
}

function upsertSkuImages(
  skuSets: SkuImageSetEntry[] | undefined,
  optionValueCodes: string[],
  images: DealImageEntryUI[],
): SkuImageSetEntry[] {
  const base = skuSets ?? [];
  const key = optionValueCodes.join('|');
  if (images.length === 0) {
    return base.filter((s) => s.optionValueCodes.join('|') !== key);
  }
  const found = base.findIndex((s) => s.optionValueCodes.join('|') === key);
  if (found === -1) return [...base, { optionValueCodes, images }];
  const next = [...base];
  next[found] = { optionValueCodes, images };
  return next;
}

// ─── SkuGridProps ─────────────────────────────────────────────────────────────

interface SkuGridProps {
  axes: AxisSpec[];
  skus: SkuRow[];
  onChange: (skus: SkuRow[]) => void;
  /** Per-SKU image sets from imageSet.skus form field. */
  imageSetSkus?: SkuImageSetEntry[];
  /** Index of the visual axis (imageSet.visualAxisOrder). null = per-row dialog mode. */
  visualAxisOrder?: number | null;
  /** Maximum images allowed per SKU. */
  maxImagesPerSku?: number;
  /** Called when imageSet.skus should be updated. */
  onImageSetSkusChange?: (next: SkuImageSetEntry[]) => void;
}

/**
 * Compute cartesian product of axis option lists.
 * Returns an array of optionValueCodes combos, one per combo.
 */
export function cartesianProduct(axes: AxisSpec[]): string[][] {
  if (axes.length === 0) return [[]];
  const [first, ...rest] = axes;
  if (!first) return [[]];
  const restCombos = cartesianProduct(rest);
  return first.options.flatMap((opt) =>
    restCombos.map((combo) => [opt.valueCode, ...combo]),
  );
}

/**
 * Merge existing SKU data with new combos produced by cartesian product.
 * Preserves existing price/qty data when a combo hash matches; initialises new rows.
 */
export function mergeSkulRows(
  axes: AxisSpec[],
  existingSkus: SkuRow[],
): SkuRow[] {
  const combos = cartesianProduct(axes);
  const existingMap = new Map(
    existingSkus.map((s) => [s.optionValueCodes.join('|'), s]),
  );
  return combos.map((codes) => {
    const key = codes.join('|');
    const existing = existingMap.get(key);
    return (
      existing ?? {
        optionValueCodes: codes,
        originalPrice: '',
        discountPercent: 50,
        discountedPrice: '',
        quantityTotal: 0,
        qtyTiers: [],
      }
    );
  });
}

// ─── SkuGrid ─────────────────────────────────────────────────────────────────

export function SkuGrid({
  axes,
  skus,
  onChange,
  imageSetSkus,
  visualAxisOrder,
  maxImagesPerSku = 5,
  onImageSetSkusChange,
}: SkuGridProps) {
  const t = useT('variants');
  const tImg = useT('vendor_images');

  // Track which SKU row dialog is open (hash = optionValueCodes.join('|'))
  const [openHash, setOpenHash] = React.useState<string | null>(null);

  function update(idx: number, patch: Partial<SkuRow>) {
    const next = skus.map((s, i) => (i === idx ? { ...s, ...patch } : s));
    onChange(next);
  }

  function computeDiscounted(original: string, pct: number): string {
    return discountedPriceStr(Number(original), pct);
  }

  if (axes.length === 0 || skus.length === 0) return null;

  // Column headers: one per axis + price columns
  const axisHeaders = axes.map((ax) => ax.nameHe || ax.nameEn || `Axis ${ax.axisOrder + 1}`);

  // Whether image management is active (we have a change handler)
  const hasImageManager = typeof onImageSetSkusChange === 'function';

  // Mode B: visual-axis fan-out — render one DealImageManager strip per axis option
  const isAxisMode = hasImageManager && typeof visualAxisOrder === 'number' && visualAxisOrder != null;
  const visualAxis = isAxisMode ? (axes[visualAxisOrder] ?? null) : null;

  return (
    <div className="space-y-4">

      {/* ── Mode B: axis fan-out section ─────────────────────────────────── */}
      {isAxisMode && visualAxis && (
        <section className="rounded-xl border border-border-default p-4">
          <h3 className="mb-3 text-sm font-semibold text-[color:var(--text)]">
            {tImg('imagesByAxis').replace('{{axis}}', visualAxis.nameHe || visualAxis.nameEn || visualAxis.kind)}
          </h3>
          <div className="space-y-5">
            {(visualAxis.options ?? []).map((opt) => {
              // All SKU rows that include this option value (as optionValueCodes arrays)
              const matchingCodes = skus
                .filter((r) => r.optionValueCodes.includes(opt.valueCode))
                .map((r) => r.optionValueCodes);
              const representative = matchingCodes[0];
              const current = representative
                ? findSkuImages(imageSetSkus, representative)
                : [];
              return (
                <div key={opt.valueCode}>
                  <p className="mb-1 text-xs font-medium text-[color:var(--text-muted)]">
                    {opt.labelHe || opt.labelEn || opt.valueCode}
                  </p>
                  <DealImageManager
                    value={current}
                    onChange={(next) => {
                      let acc: SkuImageSetEntry[] | undefined = imageSetSkus;
                      for (const codes of matchingCodes) {
                        acc = upsertSkuImages(acc, codes, next);
                      }
                      onImageSetSkusChange!(acc ?? []);
                    }}
                    max={maxImagesPerSku}
                    mandatoryOne={false}
                  />
                </div>
              );
            })}
          </div>
        </section>
      )}

      {/* ── SKU table ─────────────────────────────────────────────────────── */}
      <div className="rounded-xl border border-border-default">
        <Table>
          <Table.Head>
            <Table.Row className="bg-neutral-50">
              {axisHeaders.map((h) => (
                <Table.HeadCell
                  key={h}
                  className="text-text-muted"
                >
                  {h}
                </Table.HeadCell>
              ))}
              <Table.HeadCell className="text-text-muted">
                {t('sku_original_price')}
              </Table.HeadCell>
              <Table.HeadCell className="text-text-muted">
                {t('sku_discount')}
              </Table.HeadCell>
              <Table.HeadCell className="text-text-muted">
                {t('sku_discounted_price')}
              </Table.HeadCell>
              <Table.HeadCell className="text-text-muted">
                {t('sku_qty')}
              </Table.HeadCell>
              <Table.HeadCell className="text-text-muted">
                {t('sku_active')}
              </Table.HeadCell>
              {/* Images column only in per-row dialog mode */}
              {hasImageManager && !isAxisMode && (
                <Table.HeadCell className="text-text-muted">
                  {tImg('manageSkuImages')}
                </Table.HeadCell>
              )}
            </Table.Row>
          </Table.Head>
          <Table.Body className="divide-neutral-100">
            {skus.map((sku, idx) => {
              const hash = sku.optionValueCodes.join('|');
              // Human-readable row label for aria-label context (e.g. "XL / Red")
              const rowLabel = sku.optionValueCodes
                .map((code, axIdx) => {
                  const axis = axes[axIdx];
                  const opt = axis?.options.find((o) => o.valueCode === code);
                  return opt?.labelHe || opt?.labelEn || code;
                })
                .join(' / ');
              return (
                <Table.Row key={hash} className="bg-surface-base hover:bg-neutral-50">
                  {/* Axis label cells */}
                  {sku.optionValueCodes.map((code, axIdx) => {
                    const axis = axes[axIdx];
                    const opt = axis?.options.find((o) => o.valueCode === code);
                    return (
                      <Table.Cell
                        key={axIdx}
                        className="font-medium text-neutral-800"
                      >
                        {opt?.labelHe || opt?.labelEn || code}
                      </Table.Cell>
                    );
                  })}

                  {/* Original price */}
                  <Table.Cell className="px-2 py-1.5">
                    <Input
                      id={`sku-orig-${idx}`}
                      type="text"
                      inputMode="decimal"
                      placeholder="0.00"
                      value={sku.originalPrice}
                      className="w-24"
                      aria-label={`${t('sku_original_price')} — ${rowLabel}`}
                      onChange={(e) => {
                        const val = e.target.value;
                        update(idx, {
                          originalPrice: val,
                          discountedPrice: computeDiscounted(val, sku.discountPercent),
                        });
                      }}
                    />
                  </Table.Cell>

                  {/* Discount % */}
                  <Table.Cell className="px-2 py-1.5">
                    <NumberInput
                      id={`sku-disc-${idx}`}
                      min={50}
                      max={99}
                      value={sku.discountPercent}
                      className="w-20"
                      aria-label={`${t('sku_discount')} — ${rowLabel}`}
                      onChange={(pct) => {
                        update(idx, {
                          discountPercent: pct || 50,
                          discountedPrice: computeDiscounted(sku.originalPrice, pct || 50),
                        });
                      }}
                    />
                  </Table.Cell>

                  {/* Discounted price — read-only derived */}
                  <Table.Cell className="tabular-nums text-neutral-700">
                    {sku.discountedPrice || '—'}
                  </Table.Cell>

                  {/* Quantity */}
                  <Table.Cell className="px-2 py-1.5">
                    <NumberInput
                      id={`sku-qty-${idx}`}
                      min={0}
                      value={sku.quantityTotal}
                      className="w-20"
                      aria-label={`${t('sku_qty')} — ${rowLabel}`}
                      onChange={(n) => update(idx, { quantityTotal: n || 0 })}
                    />
                  </Table.Cell>

                  {/* Active toggle */}
                  <Table.Cell>
                    <Switch
                      id={`sku-active-${idx}`}
                      checked={sku.quantityTotal > 0}
                      aria-label={`${t('sku_active')} — ${rowLabel}`}
                      onCheckedChange={(checked) =>
                        update(idx, { quantityTotal: checked ? 1 : 0 })
                      }
                    />
                  </Table.Cell>

                  {/* ── Mode A: per-row dialog trigger ──────────────────── */}
                  {hasImageManager && !isAxisMode && (
                    <Table.Cell>
                      <Dialog.Root
                        open={openHash === hash}
                        onOpenChange={(o) => setOpenHash(o ? hash : null)}
                      >
                        <Dialog.Trigger asChild>
                          <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                          >
                            {tImg('manageSkuImages')}
                          </Button>
                        </Dialog.Trigger>
                        <Dialog.Portal>
                          <Dialog.Overlay className="fixed inset-0 z-40 bg-black/40" />
                          <Dialog.Content
                            className="fixed inset-x-0 top-1/2 z-50 mx-auto w-[min(90vw,480px)] -translate-y-1/2 rounded-xl bg-[color:var(--surface)] p-5 shadow-lg"
                            onOpenAutoFocus={(e) => e.preventDefault()}
                          >
                            <Dialog.Title className="mb-3 text-sm font-semibold text-[color:var(--text)]">
                              {sku.optionValueCodes
                                .map((code, axIdx) => {
                                  const axis = axes[axIdx];
                                  const opt = axis?.options.find((o) => o.valueCode === code);
                                  return opt?.labelHe || opt?.labelEn || code;
                                })
                                .join(' / ')}
                            </Dialog.Title>
                            <Dialog.Description className="sr-only">
                              {tImg('manageSkuImages')}
                            </Dialog.Description>
                            <DealImageManager
                              value={findSkuImages(imageSetSkus, sku.optionValueCodes)}
                              onChange={(next) =>
                                onImageSetSkusChange!(upsertSkuImages(imageSetSkus, sku.optionValueCodes, next))
                              }
                              max={maxImagesPerSku}
                              mandatoryOne={false}
                            />
                            <Dialog.Close asChild>
                              <Button
                                type="button"
                                variant="primary"
                                size="sm"
                                className="mt-4"
                              >
                                {tImg('done')}
                              </Button>
                            </Dialog.Close>
                          </Dialog.Content>
                        </Dialog.Portal>
                      </Dialog.Root>
                    </Table.Cell>
                  )}
                </Table.Row>
              );
            })}
          </Table.Body>
        </Table>
      </div>

      {/* Quantity tier sub-editors — one collapsible section per SKU row */}
      {skus.map((sku, idx) => (
        <div key={`tier-${sku.optionValueCodes.join('|')}`} className="ps-1">
          {/* Identify which SKU this tier section belongs to */}
          {axes.length > 0 && (
            <p className="mb-1 text-xs font-medium text-neutral-500">
              {sku.optionValueCodes
                .map((code, axIdx) => {
                  const axis = axes[axIdx];
                  const opt = axis?.options.find((o) => o.valueCode === code);
                  return opt?.labelHe || opt?.labelEn || code;
                })
                .join(' / ')}
            </p>
          )}
          <QtyTierEditor
            panelId={String(idx)}
            discountedPrice={sku.discountedPrice}
            tiers={sku.qtyTiers ?? []}
            onChange={(tiers) => update(idx, { qtyTiers: tiers })}
          />
        </div>
      ))}
    </div>
  );
}
