import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
  LuCheck as Check,
  LuAlertCircle as AlertCircle,
  LuLoader2 as Loader2,
  LuPlus as Plus,
  LuScanLine as ScanLine,
  LuSearch as Search,
  LuX as X,
} from 'react-icons/lu';

import {
  scanSelectedContentCheck,
  searchCatalogProducts,
} from '../../service/catalog-quality/catalog-quality.service';
import {
  CREDITS_PER_PRODUCT_SCAN,
  MAX_SCAN_PRODUCTS,
} from '../../service/catalog-quality/catalog-quality.constants';
import type { IProductSearchItem } from '../../service/catalog-quality/catalog-quality.interface';

/** Debounce window between keystrokes and the catalog search request. */
const SEARCH_DEBOUNCE_MS: number = 350;

/** Minimum query length before a search request fires. */
const SEARCH_MIN_CHARS: number = 2;

const INPUT_CLASS =
  'block w-full rounded-md border border-gray-300 py-2 pl-10 pr-10 text-sm placeholder:text-gray-400 focus:border-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-900';

/**
 * Price label for a search row; falls back to ``amount code`` when the
 * catalog carries a non-ISO currency string Intl cannot format.
 *
 * @param {number} price - Product price.
 * @param {string} currency - Currency code from the catalog feed.
 * @return {string} Display price, empty when the price is missing.
 */
const formatPriceLabel = (price: number, currency: string): string => {
  if (!price) return '';
  try {
    return new Intl.NumberFormat(undefined, {
      style: 'currency',
      currency: currency || 'EUR',
    }).format(price);
  } catch {
    return `${price} ${currency}`.trim();
  }
};

/** Props for {@link ScanProductsModal}. */
interface ScanProductsModalProps {
  open: boolean;
  onOpenChange: (next: boolean) => void;
  /** Called with the new job id once the scan has been dispatched. */
  onScanStarted: (jobId: string) => void;
}

/**
 * "Scan specific products" modal: search the catalog by name or SKU, queue
 * products one by one, then run a content-check on exactly that set. The
 * scan bills 1 credit per product; the running total is shown next to the
 * Run button. Searches fire client-side with a debounce and a stale-response
 * guard (answers for a query the merchant typed past are dropped).
 *
 * @param {ScanProductsModalProps} props - Open state + dispatch callback.
 * @return {JSX.Element | null} The modal, or null while closed.
 */
const ScanProductsModal = ({
  open,
  onOpenChange,
  onScanStarted,
}: ScanProductsModalProps): JSX.Element | null => {
  const [query, setQuery] = useState<string>('');
  const [results, setResults] = useState<IProductSearchItem[]>([]);
  const [searching, setSearching] = useState<boolean>(false);
  const [queue, setQueue] = useState<IProductSearchItem[]>([]);
  const [scanning, setScanning] = useState<boolean>(false);
  const [scanError, setScanError] = useState<string | null>(null);
  // Latest trimmed query, read when a search response lands so answers for a
  // query the merchant has already typed past get dropped.
  const latestQueryRef = useRef<string>('');

  const trimmedQuery = query.trim();

  const queuedSkus = useMemo(
    () => new Set(queue.map(item => item.sku)),
    [queue]
  );
  const queueFull = queue.length >= MAX_SCAN_PRODUCTS;
  const creditsTotal = queue.length * CREDITS_PER_PRODUCT_SCAN;

  // Reset every time the modal closes so a fresh open starts empty.
  useEffect(() => {
    if (!open) {
      setQuery('');
      setResults([]);
      setSearching(false);
      setQueue([]);
      setScanError(null);
    }
  }, [open]);

  useEffect(() => {
    if (!open) return undefined;
    const handleKey = (event: KeyboardEvent): void => {
      if (event.key === 'Escape' && !scanning) onOpenChange(false);
    };
    window.addEventListener('keydown', handleKey);

    return () => window.removeEventListener('keydown', handleKey);
  }, [open, scanning, onOpenChange]);

  useEffect(() => {
    latestQueryRef.current = trimmedQuery;
    if (!open || trimmedQuery.length < SEARCH_MIN_CHARS) {
      setResults([]);
      setSearching(false);

      return undefined;
    }
    setSearching(true);
    const handle = window.setTimeout(async () => {
      const found = await searchCatalogProducts(trimmedQuery).catch(
        () => [] as IProductSearchItem[]
      );
      // Drop answers for a query the merchant has already typed past.
      if (latestQueryRef.current !== trimmedQuery) return;
      setResults(found);
      setSearching(false);
    }, SEARCH_DEBOUNCE_MS);

    return () => window.clearTimeout(handle);
  }, [open, trimmedQuery]);

  const addToQueue = (product: IProductSearchItem): void => {
    if (queuedSkus.has(product.sku) || queueFull) return;
    setQueue(previous => [...previous, product]);
  };

  const removeFromQueue = (sku: string): void => {
    setQueue(previous => previous.filter(item => item.sku !== sku));
  };

  const runScan = async (): Promise<void> => {
    if (queue.length === 0 || scanning) return;
    setScanning(true);
    setScanError(null);
    try {
      const result = await scanSelectedContentCheck(
        queue.map(item => item.sku)
      );
      if (result.job) {
        onScanStarted(result.job.job_id);

        return;
      }
      setScanError(
        result.error ?? 'Failed to start the scan. Please try again.'
      );
    } catch {
      setScanError('Failed to start the scan. Please try again.');
    } finally {
      setScanning(false);
    }
  };

  if (!open) return null;

  return (
    <div className="fixed inset-0 z-[1000] flex items-center justify-center p-4">
      <div
        className="absolute inset-0 bg-black/40"
        onClick={() => {
          if (!scanning) onOpenChange(false);
        }}
      />
      <div
        role="dialog"
        aria-modal="true"
        aria-label="Scan specific products"
        className="relative z-10 max-h-[85vh] w-full max-w-3xl overflow-y-auto rounded-xl bg-white p-6 shadow-xl"
      >
        <div>
          <h3 className="flex items-center gap-2 text-base font-semibold text-gray-900">
            <ScanLine className="h-4 w-4 text-[#B7007C]" />
            Scan specific products
          </h3>
          <p className="mt-1 text-sm text-gray-500">
            Search your catalog by name or SKU, add products to the queue, then
            run a check on exactly that set. Each product costs{' '}
            {CREDITS_PER_PRODUCT_SCAN} credit.
          </p>
        </div>

        <div className="mt-5 space-y-4">
          <div className="relative">
            <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
            <input
              type="text"
              value={query}
              onChange={event => setQuery(event.target.value)}
              placeholder="Search products by name or SKU..."
              className={INPUT_CLASS}
              autoFocus
            />
            {searching && (
              <Loader2 className="absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-gray-400" />
            )}
          </div>

          {trimmedQuery.length >= SEARCH_MIN_CHARS && (
            <div className="max-h-64 overflow-y-auto rounded-lg border border-gray-200">
              {results.length === 0 && !searching ? (
                <p className="px-4 py-6 text-center text-sm text-gray-500">
                  No products match this search.
                </p>
              ) : (
                <ul className="divide-y divide-gray-100">
                  {results.map(product => {
                    const added = queuedSkus.has(product.sku);
                    const priceLabel = formatPriceLabel(
                      product.price,
                      product.currency
                    );

                    return (
                      <li
                        key={product.sku}
                        className="flex items-center gap-3 px-3 py-2"
                      >
                        {product.image_url ? (
                          <img
                            src={product.image_url}
                            alt=""
                            loading="lazy"
                            className="h-9 w-9 shrink-0 rounded-md border border-gray-200 object-cover"
                          />
                        ) : (
                          <div className="h-9 w-9 shrink-0 rounded-md border border-gray-200 bg-gray-100" />
                        )}
                        <div className="min-w-0 flex-1">
                          <p className="truncate text-sm font-medium text-gray-900">
                            {product.product_name || product.sku}
                          </p>
                          <p className="truncate text-xs text-gray-500">
                            SKU {product.sku}
                            {priceLabel && ` · ${priceLabel}`}
                            {!product.in_stock && ' · out of stock'}
                          </p>
                        </div>
                        <button
                          type="button"
                          disabled={added || queueFull}
                          onClick={() => addToQueue(product)}
                          className={`inline-flex shrink-0 items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs font-medium transition-colors disabled:pointer-events-none ${
                            added
                              ? 'border-emerald-200 bg-emerald-50 text-emerald-700'
                              : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50 disabled:opacity-60'
                          }`}
                        >
                          {added ? (
                            <>
                              <Check className="h-3 w-3 text-emerald-600" />
                              Added
                            </>
                          ) : (
                            <>
                              <Plus className="h-3 w-3" />
                              Add
                            </>
                          )}
                        </button>
                      </li>
                    );
                  })}
                </ul>
              )}
            </div>
          )}

          <div className="rounded-lg border border-gray-200 bg-white">
            <div className="flex items-center justify-between border-b border-gray-200 px-4 py-2.5">
              <p className="text-xs font-semibold uppercase tracking-wider text-gray-500">
                Scan queue · {queue.length}/{MAX_SCAN_PRODUCTS}
              </p>
              {queue.length > 0 && (
                <button
                  type="button"
                  onClick={() => setQueue([])}
                  className="text-xs font-medium text-gray-500 transition-colors hover:text-gray-900"
                >
                  Clear all
                </button>
              )}
            </div>
            {queue.length === 0 ? (
              <p className="px-4 py-6 text-center text-sm text-gray-500">
                Nothing queued yet. Add products from the search results above.
              </p>
            ) : (
              <ul className="max-h-48 divide-y divide-gray-100 overflow-y-auto">
                {queue.map(product => (
                  <li
                    key={product.sku}
                    className="flex items-center gap-3 px-4 py-2"
                  >
                    <div className="min-w-0 flex-1">
                      <p className="truncate text-sm text-gray-900">
                        {product.product_name || product.sku}
                      </p>
                      <p className="truncate text-xs text-gray-500">
                        SKU {product.sku}
                      </p>
                    </div>
                    <button
                      type="button"
                      onClick={() => removeFromQueue(product.sku)}
                      className="rounded-md p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-700"
                      aria-label={`Remove ${product.product_name || product.sku} from the queue`}
                    >
                      <X className="h-3.5 w-3.5" />
                    </button>
                  </li>
                ))}
              </ul>
            )}
          </div>

          {queueFull && (
            <p className="text-xs text-amber-600">
              Queue limit reached: a scan covers at most {MAX_SCAN_PRODUCTS}{' '}
              products per run.
            </p>
          )}

          {scanError && (
            <div
              role="alert"
              className="flex items-start gap-2 rounded-lg border border-amber-300 bg-amber-50 p-3"
            >
              <AlertCircle className="mt-0.5 h-4 w-4 shrink-0 text-amber-600" />
              <p className="text-sm text-amber-900">{scanError}</p>
            </div>
          )}

          <div className="flex flex-wrap items-center justify-between gap-3 border-t border-gray-100 pt-4">
            <p className="text-sm text-gray-500">
              {queue.length === 0 ? (
                `${CREDITS_PER_PRODUCT_SCAN} credit per product`
              ) : (
                <>
                  <span className="font-semibold text-gray-900">
                    {queue.length}
                  </span>{' '}
                  product{queue.length !== 1 ? 's' : ''} ·{' '}
                  <span className="font-semibold text-gray-900">
                    {creditsTotal}
                  </span>{' '}
                  credit{creditsTotal !== 1 ? 's' : ''}
                </>
              )}
            </p>
            <button
              type="button"
              onClick={runScan}
              disabled={queue.length === 0 || scanning}
              className="inline-flex items-center gap-2 rounded-md bg-[#B7007C] px-5 py-2 text-sm font-semibold text-white transition-colors hover:bg-[#900063] disabled:opacity-60"
            >
              {scanning ? (
                <>
                  <Loader2 className="h-4 w-4 animate-spin" />
                  Starting scan...
                </>
              ) : (
                <>
                  <ScanLine className="h-4 w-4" />
                  Run scan
                </>
              )}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

export default ScanProductsModal;
