import { useEffect, useRef, useState } from 'react';
import { SettingsInput } from './ui/settings-input';
import { X, Printer, HardDrive, Network, Box, FolderOpen, MoreVertical, RotateCcw } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { Swiper, SwiperSlide } from 'swiper/react';
import type { Swiper as SwiperType } from 'swiper';
import 'swiper/css';
import { toast } from 'sonner';
import { fileExists, getFileContents, joinPath, stat } from '../webdav';
import MediaBrowser from './MediaBrowser';
import MediaSet, { mediaSetEntryUrl, type MediaSetEntry } from './MediaSet';

export interface Device {
  id: string;
  number: string;
  type: 'printer' | 'drive' | 'network' | 'other' | 'meatloaf';
  name?: string;
  enabled: boolean | number;
  url?: string;
  mode?: number;
  physical?: boolean;
  physicalModel?: string;
}

interface DeviceDetailOverlayProps {
  devices: Device[];
  initialIndex: number;
  config: any;
  setConfig: (config: any) => void;
  onClose: () => void;
  onIndexChange?: (index: number) => void;
}

function DeviceIcon({ device }: { device: Device }) {
  const cls = `w-6 h-6`;
  switch (device.type) {
    case 'printer': return <Printer className={cls} />;
    case 'drive':   return <HardDrive className={cls} />;
    case 'network': return <Network className={cls} />;
    default:        return <Box className={cls} />;
  }
}

// ── Per-device slide content ─────────────────────────────────────────────────

interface DeviceCardProps {
  device: Device;
  config: any;
  setConfig: (c: any) => void;
  isActive: boolean;
}

function DeviceCard({ device, config, setConfig, isActive }: DeviceCardProps) {
  const [browsingField, setBrowsingField] = useState<'url' | 'base_url' | 'cache' | null>(null);
  const [mediaSetFiles, setMediaSetFiles] = useState<MediaSetEntry[] | null>(null);
  const detectTokenRef = useRef(0);

  // Close any open browser when swiped away.
  useEffect(() => {
    if (!isActive) setBrowsingField(null);
  }, [isActive]);

  const getDevicePath = (): string[] => ['devices', 'iec', device.number];

  const getDeviceData = () => {
    let current: any = config;
    for (const key of getDevicePath()) current = current?.[key];
    return current || {};
  };

  const deviceData = getDeviceData();

  const updateDeviceSetting = (path: string[], value: any) => {
    const newConfig = JSON.parse(JSON.stringify(config));
    let current = newConfig;
    for (let i = 0; i < path.length - 1; i++) current = current[path[i]];
    current[path[path.length - 1]] = value;
    setConfig(newConfig);
  };

  useEffect(() => {
    ++detectTokenRef.current;
    setMediaSetFiles(null);
  }, [device.number]);

  useEffect(() => {
    if (Array.isArray(deviceData.media_set) && deviceData.media_set.length > 1) {
      setMediaSetFiles(deviceData.media_set as MediaSetEntry[]);
    }
  }, [deviceData.media_set]);

  const detectMediaSet = async (url: string) => {
    const token = ++detectTokenRef.current;
    setMediaSetFiles(null);
    const match = url.match(/^(.+?)(\d+)(\.[^.]+)$/);
    if (!match) return;
    const [, prefix, , ext] = match;
    const candidates: string[] = [];
    for (let i = 1; i <= 10; i++) candidates.push(`${prefix}${i}${ext}`);
    const flags = await Promise.all(candidates.map(f => stat(f).then(r => r !== null).catch(() => false)));
    if (detectTokenRef.current !== token) return;
    const found = candidates.filter((_, i) => flags[i]);
    setMediaSetFiles(found.length > 1 ? found : null);
  };

  const switchMedia = (file: string) => updateDeviceSetting([...getDevicePath(), 'url'], file);

  const isOutsideBase = (url: string, baseUrl: string): boolean => {
    if (!baseUrl || !url.startsWith('/')) return false;
    const nb = baseUrl.endsWith('/') ? baseUrl : baseUrl + '/';
    return url !== baseUrl && !url.startsWith(nb);
  };

  const clearBaseAndCache = (dev: any) => {
    if ('base_url' in dev) dev.base_url = '';
    if ('cache' in dev) dev.cache = '';
  };

  const handleFileSelect = async (selectedPath: string) => {
    const devicePath = getDevicePath();
    const selExt = selectedPath.split('.').pop()?.toLowerCase() ?? '';
    if (selExt === 'lst' || selExt === 'vms') {
      const isVms = selExt === 'vms';
      try {
        const text = await (await getFileContents(selectedPath)).text();
        const dir = selectedPath.split('/').slice(0, -1).join('/') || '/';
        const candidates: MediaSetEntry[] = text.split('\n')
          .map(l => l.trim()).filter(l => l.length > 0 && !l.startsWith('#'))
          .map(l => {
            if (isVms) {
              const comma = l.indexOf(',');
              if (comma > 0) {
                const url = l.slice(0, comma).trim();
                const name = l.slice(comma + 1).trim();
                const resolved = url.startsWith('/') ? url : joinPath(dir, url);
                return name ? { url: resolved, name } : resolved;
              }
            }
            return l.startsWith('/') ? l : joinPath(dir, l);
          });
        if (candidates.length === 0) { toast.error('Swap list is empty'); return; }
        const existsArr = await Promise.all(candidates.map(e => fileExists(mediaSetEntryUrl(e)).catch(() => false)));
        const files = candidates.filter((_, i) => existsArr[i]);
        if (files.length === 0) { toast.error('No files in swap list exist on device'); return; }
        if (files.length < 2) { toast.error('A media set needs more than one item'); return; }
        if (files.length < candidates.length)
          toast.warning(`${candidates.length - files.length} missing file(s) skipped from swap list`);
        const newConfig = JSON.parse(JSON.stringify(config));
        let dev = newConfig;
        for (const k of devicePath) dev = dev[k];
        if (isOutsideBase(mediaSetEntryUrl(files[0]), dev.base_url || '')) clearBaseAndCache(dev);
        dev.url = mediaSetEntryUrl(files[0]);
        dev.media_set = files;
        setConfig(newConfig);
      } catch (e: any) {
        toast.error(`Failed to read swap list: ${e?.message ?? e}`);
      }
    } else {
      const newConfig = JSON.parse(JSON.stringify(config));
      let dev = newConfig;
      for (const k of devicePath) dev = dev[k];
      if (isOutsideBase(selectedPath, dev.base_url || '')) clearBaseAndCache(dev);
      dev.url = selectedPath;
      delete dev.media_set;
      setConfig(newConfig);
      void detectMediaSet(selectedPath);
    }
  };

  return (
    <>
      <div className="p-4 space-y-6">
        <div className="space-y-4">
          {!device.physical && (
            <div className="flex items-center justify-between">
              <label className="text-sm text-neutral-500">Enabled</label>
              <button
                onClick={() => updateDeviceSetting([...getDevicePath(), 'enabled'], deviceData.enabled ? 0 : 1)}
                className={`relative w-12 h-6 rounded-full transition-colors ${deviceData.enabled ? 'bg-blue-600' : 'bg-neutral-300'}`}
              >
                <div className={`absolute top-0.5 w-5 h-5 bg-white rounded-full transition-transform ${deviceData.enabled ? 'translate-x-6' : 'translate-x-0.5'}`} />
              </button>
            </div>
          )}

          <div>
            <label className="text-sm text-neutral-500 block mb-2">Type</label>
            <div className="px-3 py-2 bg-neutral-50 border border-neutral-200 rounded-lg text-neutral-700">
              {device.type.charAt(0).toUpperCase() + device.type.slice(1)}
            </div>
          </div>

          <div>
            <label className="text-sm text-neutral-500 block mb-2">Device Name</label>
            <SettingsInput
              type="text"
              value={deviceData.name || device.name || `Device ${device.number}`}
              onCommit={(v) => updateDeviceSetting([...getDevicePath(), 'name'], v)}
              onClear={() => updateDeviceSetting([...getDevicePath(), 'name'], '')}
              className="px-3 py-2 border border-neutral-300 rounded-lg"
              containerClassName="w-full"
            />
          </div>

          {!device.physical && <>
            <div>
              <label className="text-sm text-neutral-500 block mb-2">Base URL</label>
              <div className="flex gap-2">
                <SettingsInput
                  type="text"
                  value={deviceData.base_url ?? ''}
                  onCommit={(v) => updateDeviceSetting([...getDevicePath(), 'base_url'], v)}
                  onClear={() => updateDeviceSetting([...getDevicePath(), 'base_url'], '')}
                  containerClassName="flex-1"
                  className="px-3 py-2 border border-neutral-300 rounded-lg"
                />
                <button
                  onClick={() => setBrowsingField('base_url')}
                  className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-neutral-100"
                >
                  <FolderOpen className="w-5 h-5" />
                </button>
              </div>
            </div>

            <div>
              <label className="text-sm text-neutral-500 block mb-2">URL</label>
              <div className="flex gap-2">
                <SettingsInput
                  type="text"
                  value={deviceData.url ?? ''}
                  onCommit={(newUrl) => {
                    const devicePath = getDevicePath();
                    const newConfig = JSON.parse(JSON.stringify(config));
                    let dev = newConfig;
                    for (const k of devicePath) dev = dev[k];
                    if (isOutsideBase(newUrl, dev.base_url || '')) clearBaseAndCache(dev);
                    dev.url = newUrl;
                    delete dev.media_set;
                    setConfig(newConfig);
                    if (newUrl) void detectMediaSet(newUrl);
                  }}
                  onClear={() => {
                    const devicePath = getDevicePath();
                    const newConfig = JSON.parse(JSON.stringify(config));
                    let dev = newConfig;
                    for (const k of devicePath) dev = dev[k];
                    delete dev.url;
                    delete dev.media_set;
                    setConfig(newConfig);
                    ++detectTokenRef.current;
                    setMediaSetFiles(null);
                  }}
                  containerClassName="flex-1"
                  className="px-3 py-2 border border-neutral-300 rounded-lg"
                />
                <button
                  onClick={() => setBrowsingField('url')}
                  className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-neutral-100"
                >
                  <FolderOpen className="w-5 h-5" />
                </button>
              </div>
              {mediaSetFiles && (
                <div className="mt-3">
                  <MediaSet files={mediaSetFiles} activeUrl={deviceData.url ?? ''} onSwitch={switchMedia} />
                </div>
              )}
            </div>

            {(deviceData.cache !== undefined ||
              (deviceData.base_url ?? '').includes('://') ||
              (deviceData.url ?? '').includes('://')) && (
              <div>
                <label className="text-sm text-neutral-500 block mb-2">Cache</label>
                <div className="flex gap-2">
                  <SettingsInput
                    type="text"
                    value={deviceData.cache ?? ''}
                    onCommit={(v) => updateDeviceSetting([...getDevicePath(), 'cache'], v)}
                    onClear={() => updateDeviceSetting([...getDevicePath(), 'cache'], '')}
                    containerClassName="flex-1"
                    className="px-3 py-2 border border-neutral-300 rounded-lg"
                  />
                  <button
                    onClick={() => setBrowsingField('cache')}
                    className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-neutral-100"
                  >
                    <FolderOpen className="w-5 h-5" />
                  </button>
                </div>
              </div>
            )}
          </>}

          {deviceData.mode !== undefined && (
            <div className="flex items-center justify-between">
              <label className="text-sm text-neutral-500">Mode</label>
              {device.physical
                ? <span className="text-sm text-neutral-700 px-3 py-2">{(deviceData.mode ?? 0) === 0 ? 'Read Only' : 'Write Enabled'}</span>
                : <div className="flex rounded-lg border border-neutral-300 overflow-hidden text-sm">
                    {([0, 1] as const).map((val, i) => (
                      <button
                        key={val}
                        onClick={() => updateDeviceSetting([...getDevicePath(), 'mode'], val)}
                        className={`px-4 py-2 ${i > 0 ? 'border-l border-neutral-300' : ''} ${(deviceData.mode ?? 0) === val ? 'bg-blue-600 text-white' : 'bg-white text-neutral-700 hover:bg-neutral-50'}`}
                      >
                        {val === 0 ? 'Read Only' : 'Write Enabled'}
                      </button>
                    ))}
                  </div>
              }
            </div>
          )}

          {deviceData.baud !== undefined && (
            <div>
              <label className="text-sm text-neutral-500 block mb-2">Baud Rate</label>
              <SettingsInput
                type="number"
                value={String(deviceData.baud ?? '')}
                onCommit={(v) => updateDeviceSetting([...getDevicePath(), 'baud'], parseInt(v))}
                className="w-full px-3 py-2 border border-neutral-300 rounded-lg"
              />
            </div>
          )}
        </div>

        <div className="pt-4 border-t border-neutral-200">
          <h3 className="text-sm text-neutral-500 mb-2">Device ID</h3>
          <code className="text-xs text-neutral-600 bg-neutral-50 px-2 py-1 rounded">{device.id}</code>
        </div>
      </div>

      {browsingField && (
        <MediaBrowser
          currentPath={
            browsingField === 'cache'    ? (deviceData.cache    || '/') :
            browsingField === 'base_url' ? (deviceData.base_url || '/') :
            (deviceData.url || '/')
          }
          onSelect={(selectedPath: string) => {
            if (browsingField === 'url') {
              void handleFileSelect(selectedPath);
            } else {
              updateDeviceSetting([...getDevicePath(), browsingField], selectedPath);
            }
            setBrowsingField(null);
          }}
          onClose={() => setBrowsingField(null)}
        />
      )}
    </>
  );
}

// ── Overlay shell with Swiper ────────────────────────────────────────────────

export default function DeviceDetailOverlay({
  devices,
  initialIndex,
  config,
  setConfig,
  onClose,
  onIndexChange,
}: DeviceDetailOverlayProps) {
  const [activeIndex, setActiveIndex] = useState(initialIndex);
  const [showCommandMenu, setShowCommandMenu] = useState(false);
  const swiperRef = useRef<SwiperType | null>(null);

  const activeDevice = devices[activeIndex] ?? devices[0];

  useEffect(() => { setShowCommandMenu(false); }, [activeIndex]);

  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if (e.key === 'Escape') { onClose(); return; }
      const tag = (document.activeElement as HTMLElement)?.tagName;
      if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
      if (e.key === 'ArrowLeft')  swiperRef.current?.slidePrev();
      if (e.key === 'ArrowRight') swiperRef.current?.slideNext();
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [onClose]);

  return (
    <AnimatePresence>
      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        className="fixed inset-0 bg-black/50 z-50"
        onClick={onClose}
      >
        <motion.div
          initial={{ y: '100%' }}
          animate={{ y: 0 }}
          exit={{ y: '100%' }}
          transition={{ type: 'spring', damping: 30, stiffness: 300 }}
          className="fixed inset-0 bg-white flex flex-col z-50"
          onClick={(e) => e.stopPropagation()}
        >
          {/* ── Header ── */}
          <div className="flex-shrink-0 border-b border-neutral-200 relative">
            <div className="flex items-center justify-between p-4">
              <button onClick={onClose} className="p-2 -m-2">
                <X className="w-6 h-6" />
              </button>

              <div className={`flex flex-col items-center gap-0.5 ${
                activeDevice.physical ? 'text-green-600' : activeDevice.enabled ? 'text-blue-600' : 'text-neutral-400'
              }`}>
                <span className="text-sm font-semibold leading-none">{activeDevice.number}</span>
                <DeviceIcon device={activeDevice} />
                {activeDevice.physical && (
                  <span className="text-xs text-green-700 px-1 py-0.5 bg-green-50 border border-green-200 rounded leading-none">
                    Physical
                  </span>
                )}
              </div>

              <div className="flex items-center gap-2">
                {devices.length > 1 && (
                  <span className="text-xs text-neutral-400 tabular-nums">
                    {activeIndex + 1} / {devices.length}
                  </span>
                )}
                <button onClick={() => setShowCommandMenu(v => !v)} className="p-2 -m-2">
                  <MoreVertical className="w-6 h-6" />
                </button>
              </div>
            </div>

            {showCommandMenu && (
              <div className="absolute right-4 top-14 bg-white rounded-lg shadow-lg border border-neutral-200 py-2 min-w-[200px] z-20">
                <button
                  onClick={() => {
                    console.log(`Reset device ${activeDevice.number}`);
                    setShowCommandMenu(false);
                  }}
                  className="w-full px-4 py-2 text-left hover:bg-neutral-50 flex items-center gap-2"
                >
                  <RotateCcw className="w-4 h-4" />
                  Reset Device
                </button>
              </div>
            )}
          </div>

          {/* ── Swiper ── */}
          <div className="flex-1 min-h-0">
            <Swiper
              initialSlide={initialIndex}
              onSwiper={(s) => { swiperRef.current = s; }}
              onSlideChange={(s) => {
                setActiveIndex(s.activeIndex);
                onIndexChange?.(s.activeIndex);
              }}
              touchStartPreventDefault={false}
              style={{ height: '100%' }}
            >
              {devices.map((device, i) => (
                <SwiperSlide key={device.id} style={{ overflowY: 'auto', height: '100%' }}>
                  <DeviceCard
                    device={device}
                    config={config}
                    setConfig={setConfig}
                    isActive={i === activeIndex}
                  />
                </SwiperSlide>
              ))}
            </Swiper>
          </div>
        </motion.div>
      </motion.div>
    </AnimatePresence>
  );
}
