import { useState, useMemo } from 'react';
import { useQueryClient, useQueries } from '@tanstack/react-query';
import {
  Printer as PrinterIcon,
  Loader2,
  AlertCircle,
  AlertTriangle,
  Check,
  Circle,
  RefreshCw,
  Wand2,
  Users,
} from 'lucide-react';
import { api, type PrinterStatus } from '../../api/client';
import { getColorName } from '../../utils/colors';
import {
  normalizeColorForCompare,
  colorsAreSimilar,
  autoMatchFilament,
  filterFilamentsByNozzle,
} from '../../utils/amsHelpers';
import type { PrinterSelectorProps, AssignmentMode } from './types';
import type { PrinterMappingResult, PerPrinterConfig } from '../../hooks/useMultiPrinterFilamentMapping';
import type { FilamentRequirement, LoadedFilament } from '../../hooks/useFilamentMapping';

interface PrinterSelectorWithMappingProps extends PrinterSelectorProps {
  /** Per-printer mapping results (only used when multiple printers selected) */
  printerMappingResults?: PrinterMappingResult[];
  /** Filament requirements for the print */
  filamentReqs?: { filaments: FilamentRequirement[] };
  /** Callback to auto-configure a printer */
  onAutoConfigurePrinter?: (printerId: number) => void;
  /** Callback to update printer config */
  onUpdatePrinterConfig?: (printerId: number, config: Partial<PerPrinterConfig>) => void;
  /** Current assignment mode */
  assignmentMode?: AssignmentMode;
  /** Handler for assignment mode change */
  onAssignmentModeChange?: (mode: AssignmentMode) => void;
  /** Selected target model (when assignmentMode is 'model') */
  targetModel?: string | null;
  /** Handler for target model change */
  onTargetModelChange?: (model: string | null) => void;
  /** Selected target location (when assignmentMode is 'model') */
  targetLocation?: string | null;
  /** Handler for target location change */
  onTargetLocationChange?: (location: string | null) => void;
  /** Suggested model from sliced file (for pre-selection) */
  slicedForModel?: string | null;
}

/** States where the printer is available to accept a new print */
const AVAILABLE_STATES = new Set(['IDLE', 'FINISH', 'FAILED']);

/**
 * Inline AMS mapping editor for a single printer.
 */
function InlineMappingEditor({
  printerResult,
  filamentReqs,
  onUpdateConfig,
}: {
  printerResult: PrinterMappingResult;
  filamentReqs: FilamentRequirement[];
  onUpdateConfig: (config: Partial<PerPrinterConfig>) => void;
}) {
  const queryClient = useQueryClient();
  const [isRefreshing, setIsRefreshing] = useState(false);

  const handleSlotChange = (slotId: number, value: string) => {
    if (slotId <= 0) return;

    const newMappings = { ...printerResult.config.manualMappings };
    if (value === '') {
      delete newMappings[slotId];
    } else {
      newMappings[slotId] = parseInt(value, 10);
    }

    onUpdateConfig({
      useDefault: false,
      manualMappings: newMappings,
      autoConfigured: false,
    });
  };

  const handleRefresh = async () => {
    setIsRefreshing(true);
    try {
      await api.refreshPrinterStatus(printerResult.printerId);
      await new Promise((r) => setTimeout(r, 500));
      await queryClient.refetchQueries({ queryKey: ['printer-status', printerResult.printerId] });
    } finally {
      setIsRefreshing(false);
    }
  };

  // Compute current slot assignments
  const slotAssignments = filamentReqs.map((req) => {
    const slotId = req.slot_id || 0;
    const currentMapping = printerResult.config.manualMappings[slotId];

    let loaded: LoadedFilament | undefined;
    let isManual = false;

    if (currentMapping !== undefined) {
      loaded = printerResult.loadedFilaments.find((f) => f.globalTrayId === currentMapping);
      isManual = true;
    } else {
      const usedTrayIds = new Set<number>(Object.values(printerResult.config.manualMappings));
      const cachedSettings = queryClient.getQueryData<{ prefer_lowest_filament?: boolean }>(['settings']);
      loaded = autoMatchFilament(req, printerResult.loadedFilaments, usedTrayIds, cachedSettings?.prefer_lowest_filament) as LoadedFilament | undefined;
    }

    // Determine status
    let status: 'match' | 'type_only' | 'mismatch' = 'mismatch';
    if (loaded) {
      const typeMatch = loaded.type?.toUpperCase() === req.type?.toUpperCase();
      const colorMatch =
        normalizeColorForCompare(loaded.color) === normalizeColorForCompare(req.color) ||
        colorsAreSimilar(loaded.color, req.color);

      if (typeMatch && colorMatch) {
        status = 'match';
      } else if (typeMatch) {
        status = 'type_only';
      }
    }

    return { req, loaded, status, isManual };
  });

  return (
    <div className="mt-2 bg-bambu-dark rounded-lg p-3 space-y-2">
      <div className="flex items-center justify-between mb-2">
        <span className="text-xs text-bambu-gray">Custom slot mapping</span>
        <button
          type="button"
          onClick={handleRefresh}
          className="flex items-center gap-1 px-2 py-0.5 text-xs rounded border border-bambu-gray/30 hover:border-bambu-gray hover:bg-bambu-dark-tertiary transition-colors text-bambu-gray hover:text-white"
          disabled={isRefreshing}
        >
          <RefreshCw className={`w-3 h-3 ${isRefreshing ? 'animate-spin' : ''}`} />
          <span>Re-read</span>
        </button>
      </div>

      {slotAssignments.map(({ req, loaded, status, isManual }, idx) => (
        <div
          key={idx}
          className="grid items-center gap-2 text-xs"
          style={{ gridTemplateColumns: '16px minmax(70px, 1fr) auto 2fr 16px' }}
        >
          <span title={`Required: ${req.type} - ${getColorName(req.color)}`}>
            <Circle className="w-3 h-3" fill={req.color} stroke={req.color} />
          </span>
          <span className="text-white truncate">
            {req.type} <span className="text-bambu-gray">({req.used_grams}g)</span>
          </span>
          <span className="text-bambu-gray">→</span>
          <select
            value={loaded?.globalTrayId ?? ''}
            onChange={(e) => handleSlotChange(req.slot_id || 0, e.target.value)}
            className={`flex-1 px-2 py-1 rounded border text-xs bg-bambu-dark-secondary focus:outline-none focus:ring-1 focus:ring-bambu-green ${
              status === 'match'
                ? 'border-bambu-green/50 text-bambu-green'
                : status === 'type_only'
                ? 'border-yellow-400/50 text-yellow-400'
                : 'border-orange-400/50 text-orange-400'
            } ${isManual ? 'ring-1 ring-blue-400/50' : ''}`}
            title={isManual ? 'Manually selected' : 'Auto-matched'}
          >
            <option value="" className="bg-bambu-dark text-bambu-gray">
              -- Select slot --
            </option>
            {filterFilamentsByNozzle(printerResult.loadedFilaments, req.nozzle_id)
              .map((f) => (
              <option key={f.globalTrayId} value={f.globalTrayId} className="bg-bambu-dark text-white">
                {f.label}: {f.traySubBrands || f.type} ({f.colorName})
              </option>
            ))}
          </select>
          {status === 'match' ? (
            <Check className="w-3 h-3 text-bambu-green" />
          ) : status === 'type_only' ? (
            <span title="Same type, different color">
              <AlertTriangle className="w-3 h-3 text-yellow-400" />
            </span>
          ) : (
            <span title="Filament type not loaded">
              <AlertTriangle className="w-3 h-3 text-orange-400" />
            </span>
          )}
        </div>
      ))}
    </div>
  );
}

/**
 * Printer selection component with grid-based UI.
 * Supports single or multi-select modes.
 * When multiple printers are selected, shows per-printer mapping overrides.
 */
export function PrinterSelector({
  printers,
  selectedPrinterIds,
  onMultiSelect,
  isLoading = false,
  allowMultiple = false,
  showInactive = false,
  disableBusy = false,
  printerMappingResults,
  filamentReqs,
  onAutoConfigurePrinter,
  onUpdatePrinterConfig,
  assignmentMode = 'printer',
  onAssignmentModeChange,
  targetModel,
  onTargetModelChange,
  targetLocation,
  onTargetLocationChange,
  slicedForModel,
}: PrinterSelectorWithMappingProps) {
  // State for showing all printers vs only matching model
  const [showAllPrinters, setShowAllPrinters] = useState(false);

  // Filter printers based on showInactive flag
  const activePrinters = showInactive ? printers : printers.filter((p) => p.is_active);

  // Fetch printer statuses to determine busy/idle state
  const statusQueries = useQueries({
    queries: activePrinters.map((printer) => ({
      queryKey: ['printerStatus', printer.id],
      queryFn: () => api.getPrinterStatus(printer.id),
      staleTime: 5000,
    })),
  });

  // Build a map of printer ID -> status for quick lookup
  const printerStatusMap = useMemo(() => {
    const map = new Map<number, PrinterStatus>();
    activePrinters.forEach((printer, idx) => {
      const query = statusQueries[idx];
      if (query?.data) {
        map.set(printer.id, query.data);
      }
    });
    return map;
  }, [activePrinters, statusQueries]);

  const isPrinterBusy = (printerId: number): boolean => {
    const status = printerStatusMap.get(printerId);
    if (!status) return false; // Unknown state — don't block
    if (!status.connected) return true;
    return !AVAILABLE_STATES.has(status.state ?? '');
  };

  const getPrinterStateLabel = (printerId: number): string | null => {
    const status = printerStatusMap.get(printerId);
    if (!status) return null;
    if (!status.connected) return 'Offline';
    const state = status.state;
    if (!state) return null;
    if (state === 'RUNNING') return status.stg_cur_name || 'Printing';
    if (state === 'PREPARE') return 'Preparing';
    if (state === 'PAUSE') return 'Paused';
    if (state === 'IDLE') return 'Idle';
    if (state === 'FINISH') return 'Finished';
    if (state === 'FAILED') return 'Failed';
    return state;
  };

  // Filter by sliced model (only in printer mode, when slicedForModel is set)
  const displayPrinters = useMemo(() => {
    if (assignmentMode !== 'printer' || !slicedForModel || showAllPrinters) {
      return activePrinters;
    }
    // Filter to only show printers matching the sliced model
    const matching = activePrinters.filter((p) => p.model === slicedForModel);
    // If no matching printers, show all
    return matching.length > 0 ? matching : activePrinters;
  }, [activePrinters, assignmentMode, slicedForModel, showAllPrinters]);

  // Check if there are hidden printers due to model filtering
  const hiddenPrinterCount = activePrinters.length - displayPrinters.length;

  // Get unique models from available printers (for model-based assignment)
  const uniqueModels = useMemo(() => {
    const models = activePrinters
      .map(p => p.model)
      .filter((m): m is string => Boolean(m));
    return [...new Set(models)].sort();
  }, [activePrinters]);

  // Get unique locations for the selected target model (for location filtering)
  const uniqueLocations = useMemo(() => {
    if (!targetModel) return [];
    const locations = activePrinters
      .filter(p => p.model === targetModel && p.location)
      .map(p => p.location)
      .filter((l): l is string => Boolean(l));
    return [...new Set(locations)].sort();
  }, [activePrinters, targetModel]);

  // Check if model-based assignment is available (need callbacks and multiple printers of same model)
  const modelAssignmentAvailable = onAssignmentModeChange && onTargetModelChange && uniqueModels.length > 0;

  const showMappingOptions = allowMultiple &&
    selectedPrinterIds.length > 1 &&
    printerMappingResults &&
    filamentReqs?.filaments &&
    filamentReqs.filaments.length > 0 &&
    onAutoConfigurePrinter &&
    onUpdatePrinterConfig;

  if (isLoading) {
    return (
      <div className="flex justify-center py-8">
        <Loader2 className="w-6 h-6 text-bambu-green animate-spin" />
      </div>
    );
  }

  if (displayPrinters.length === 0) {
    return (
      <div className="flex items-center gap-2 text-red-400 text-sm mb-4">
        <AlertCircle className="w-4 h-4" />
        No {showInactive ? '' : 'active '}printers available
      </div>
    );
  }

  const handlePrinterClick = (printerId: number) => {
    if (disableBusy && isPrinterBusy(printerId)) return;

    if (allowMultiple) {
      if (selectedPrinterIds.includes(printerId)) {
        onMultiSelect(selectedPrinterIds.filter((id) => id !== printerId));
      } else {
        onMultiSelect([...selectedPrinterIds, printerId]);
      }
    } else {
      onMultiSelect([printerId]);
    }
  };

  const handleSelectAll = () => {
    const selectable = disableBusy
      ? displayPrinters.filter((p) => !isPrinterBusy(p.id))
      : displayPrinters;
    onMultiSelect(selectable.map((p) => p.id));
  };

  const handleDeselectAll = () => {
    onMultiSelect([]);
  };

  const handleOverrideToggle = (printerId: number, enabled: boolean, e: React.MouseEvent) => {
    e.stopPropagation();
    if (!onAutoConfigurePrinter || !onUpdatePrinterConfig) return;

    if (enabled) {
      onAutoConfigurePrinter(printerId);
    } else {
      onUpdatePrinterConfig(printerId, {
        useDefault: true,
        manualMappings: {},
        autoConfigured: false,
      });
    }
  };

  const isSelected = (printerId: number) => selectedPrinterIds.includes(printerId);
  const selectedCount = selectedPrinterIds.length;

  const getPrinterMappingResult = (printerId: number) => {
    return printerMappingResults?.find((r) => r.printerId === printerId);
  };

  return (
    <div className="space-y-2 mb-6">
      {/* Assignment mode toggle (model vs specific printer) */}
      {modelAssignmentAvailable && (
        <div className="flex gap-2 mb-4">
          <button
            type="button"
            onClick={() => {
              onAssignmentModeChange!('printer');
              onTargetModelChange!(null);
            }}
            className={`flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-lg border transition-colors ${
              assignmentMode === 'printer'
                ? 'border-bambu-green bg-bambu-green/10 text-white'
                : 'border-bambu-dark-tertiary bg-bambu-dark text-bambu-gray hover:border-bambu-gray'
            }`}
          >
            <PrinterIcon className="w-4 h-4" />
            <span className="text-sm">Specific Printer</span>
          </button>
          <button
            type="button"
            onClick={() => {
              onAssignmentModeChange!('model');
              onMultiSelect([]);
              // Pre-select the sliced-for model if available, otherwise first model
              const defaultModel = slicedForModel && uniqueModels.includes(slicedForModel)
                ? slicedForModel
                : uniqueModels[0];
              onTargetModelChange!(defaultModel);
            }}
            className={`flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-lg border transition-colors ${
              assignmentMode === 'model'
                ? 'border-bambu-green bg-bambu-green/10 text-white'
                : 'border-bambu-dark-tertiary bg-bambu-dark text-bambu-gray hover:border-bambu-gray'
            }`}
          >
            <Users className="w-4 h-4" />
            <span className="text-sm">Any {slicedForModel || 'Model'}</span>
          </button>
        </div>
      )}

      {/* Model selection and location filter (when in model mode) */}
      {assignmentMode === 'model' && modelAssignmentAvailable && (
        <div className="space-y-3 mb-4">
          {/* Model selector — only show when sliced model is unknown */}
          {!slicedForModel && (
            <div>
              <label className="block text-xs text-bambu-gray mb-1">Target Model</label>
              <select
                value={targetModel || ''}
                onChange={(e) => {
                  onTargetModelChange!(e.target.value || null);
                  // Clear location when model changes
                  if (onTargetLocationChange) {
                    onTargetLocationChange(null);
                  }
                }}
                className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none text-sm"
              >
                <option value="">Select a model...</option>
                {uniqueModels.map((model) => (
                  <option key={model} value={model}>
                    {model}
                  </option>
                ))}
              </select>
            </div>
          )}

          {/* Location filter (only show when target model is selected and locations exist) */}
          {targetModel && uniqueLocations.length > 0 && onTargetLocationChange && (
            <div>
              <label className="block text-xs text-bambu-gray mb-1">Location Filter (optional)</label>
              <select
                value={targetLocation || ''}
                onChange={(e) => onTargetLocationChange(e.target.value || null)}
                className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none text-sm"
              >
                <option value="">Any location</option>
                {uniqueLocations.map((location) => (
                  <option key={location} value={location}>
                    {location}
                  </option>
                ))}
              </select>
            </div>
          )}

          {/* Info text */}
          {targetModel && (
            <p className="text-xs text-bambu-gray">
              Scheduler will assign to first available idle {targetModel} printer
              {targetLocation ? ` in ${targetLocation}` : ''}
            </p>
          )}
        </div>
      )}

      {/* Multi-select header (only in printer mode) */}
      {assignmentMode === 'printer' && allowMultiple && displayPrinters.length > 1 && (
        <div className="flex items-center justify-between text-xs text-bambu-gray mb-2">
          <span>
            {selectedCount === 0
              ? 'Select printers'
              : `${selectedCount} printer${selectedCount !== 1 ? 's' : ''} selected`}
          </span>
          <div className="flex gap-2">
            {selectedCount < displayPrinters.length && (
              <button
                type="button"
                onClick={handleSelectAll}
                className="text-bambu-green hover:text-bambu-green/80 transition-colors"
              >
                Select all
              </button>
            )}
            {selectedCount > 0 && (
              <button
                type="button"
                onClick={handleDeselectAll}
                className="text-bambu-gray hover:text-white transition-colors"
              >
                Clear
              </button>
            )}
          </div>
        </div>
      )}

      {/* Printer list (only in printer mode) */}
      {assignmentMode === 'printer' && displayPrinters.map((printer) => {
        const selected = isSelected(printer.id);
        const mappingResult = getPrinterMappingResult(printer.id);
        const hasOverride = mappingResult && !mappingResult.config.useDefault;
        const busy = isPrinterBusy(printer.id);
        const disabled = disableBusy && busy;
        const stateLabel = getPrinterStateLabel(printer.id);

        return (
          <div key={printer.id}>
            {/* Printer selection button */}
            <button
              type="button"
              onClick={() => handlePrinterClick(printer.id)}
              disabled={disabled}
              className={`w-full flex items-center gap-3 p-3 rounded-lg border transition-colors ${
                disabled
                  ? 'border-bambu-dark-tertiary bg-bambu-dark opacity-50 cursor-not-allowed'
                  : selected
                  ? 'border-bambu-green bg-bambu-green/10'
                  : 'border-bambu-dark-tertiary bg-bambu-dark hover:border-bambu-gray'
              } ${!printer.is_active ? 'opacity-60' : ''}`}
            >
              <div
                className={`p-2 rounded-lg ${
                  disabled ? 'bg-bambu-dark-tertiary' : selected ? 'bg-bambu-green/20' : 'bg-bambu-dark-tertiary'
                }`}
              >
                <PrinterIcon
                  className={`w-5 h-5 ${
                    disabled ? 'text-bambu-gray/50' : selected ? 'text-bambu-green' : 'text-bambu-gray'
                  }`}
                />
              </div>
              <div className="text-left flex-1">
                <p className={`font-medium ${disabled ? 'text-bambu-gray' : 'text-white'}`}>
                  {printer.name}
                  {!printer.is_active && <span className="text-bambu-gray text-xs ml-2">(inactive)</span>}
                </p>
                <p className="text-xs text-bambu-gray">
                  {printer.model || 'Unknown model'} • {printer.ip_address}
                </p>
              </div>
              {stateLabel && (
                <span className={`text-xs px-2 py-0.5 rounded-full ${
                  busy
                    ? 'bg-yellow-500/20 text-yellow-400'
                    : 'bg-bambu-green/20 text-bambu-green'
                }`}>
                  {stateLabel}
                </span>
              )}
              {allowMultiple && (
                <div
                  className={`w-5 h-5 rounded border-2 flex items-center justify-center transition-colors ${
                    disabled
                      ? 'border-bambu-gray/30'
                      : selected
                      ? 'bg-bambu-green border-bambu-green'
                      : 'border-bambu-gray/50'
                  }`}
                >
                  {selected && <Check className="w-3 h-3 text-white" />}
                </div>
              )}
            </button>

            {/* Per-printer override checkbox + mapping (only when selected and multi-printer) */}
            {selected && showMappingOptions && mappingResult && (
              <div className="ml-4 mt-2 mb-3">
                {/* Override checkbox row */}
                <div className="flex items-center gap-2">
                  <label
                    className="flex items-center gap-2 cursor-pointer"
                    onClick={(e) => e.stopPropagation()}
                  >
                    <input
                      type="checkbox"
                      checked={hasOverride}
                      onChange={(e) => handleOverrideToggle(printer.id, e.target.checked, e as unknown as React.MouseEvent)}
                      className="w-3.5 h-3.5 rounded border-bambu-gray/30 bg-bambu-dark-secondary text-bambu-green focus:ring-bambu-green focus:ring-offset-0"
                    />
                    <span className="text-xs text-bambu-gray">Custom mapping</span>
                  </label>

                  {/* Match status indicator */}
                  <span className={`text-xs ml-2 ${
                    mappingResult.matchStatus === 'full'
                      ? 'text-bambu-green'
                      : mappingResult.matchStatus === 'partial'
                      ? 'text-yellow-400'
                      : 'text-orange-400'
                  }`}>
                    ({mappingResult.exactMatches}/{mappingResult.totalSlots} matched)
                  </span>

                  {/* Loading indicator */}
                  {mappingResult.isLoading && (
                    <RefreshCw className="w-3 h-3 text-bambu-gray animate-spin" />
                  )}

                  {/* Auto-configure button (when override is enabled) */}
                  {hasOverride && (
                    <button
                      type="button"
                      onClick={(e) => {
                        e.stopPropagation();
                        onAutoConfigurePrinter!(printer.id);
                      }}
                      className="ml-auto flex items-center gap-1 px-2 py-0.5 text-xs rounded border border-bambu-gray/30 hover:border-bambu-gray hover:bg-bambu-dark-tertiary transition-colors text-bambu-gray hover:text-white"
                    >
                      <Wand2 className="w-3 h-3" />
                      Auto
                    </button>
                  )}
                </div>

                {/* Inline mapping editor (shown when override is checked) */}
                {hasOverride && (
                  <InlineMappingEditor
                    printerResult={mappingResult}
                    filamentReqs={filamentReqs!.filaments}
                    onUpdateConfig={(config) => onUpdatePrinterConfig!(printer.id, config)}
                  />
                )}
              </div>
            )}
          </div>
        );
      })}

      {/* Show hidden printers toggle */}
      {assignmentMode === 'printer' && hiddenPrinterCount > 0 && !showAllPrinters && (
        <button
          type="button"
          onClick={() => setShowAllPrinters(true)}
          className="text-xs text-bambu-gray hover:text-white transition-colors mt-2 flex items-center gap-1"
        >
          <AlertTriangle className="w-3 h-3 text-yellow-400" />
          {hiddenPrinterCount} other printer{hiddenPrinterCount > 1 ? 's' : ''} hidden (different model) —
          <span className="underline">show all</span>
        </button>
      )}

      {/* Show matching only toggle */}
      {assignmentMode === 'printer' && showAllPrinters && slicedForModel && (
        <button
          type="button"
          onClick={() => setShowAllPrinters(false)}
          className="text-xs text-bambu-gray hover:text-white transition-colors mt-2"
        >
          <span className="underline">Show only {slicedForModel} printers</span>
        </button>
      )}

      {/* Warning when no printer selected (only in printer mode) */}
      {assignmentMode === 'printer' && selectedCount === 0 && (
        <p className="text-xs text-orange-400 mt-1 flex items-center gap-1">
          <AlertCircle className="w-3 h-3" />
          Select at least one printer
        </p>
      )}

      {/* Warning when no model selected (only in model mode) */}
      {assignmentMode === 'model' && !targetModel && (
        <p className="text-xs text-orange-400 mt-1 flex items-center gap-1">
          <AlertCircle className="w-3 h-3" />
          Select a target printer model
        </p>
      )}
    </div>
  );
}
