import { useState, useCallback, useRef } from 'react';
import { useDraggable } from '@dnd-kit/core';
import { GripHorizontal } from 'lucide-react';
import { toast } from 'sonner';
import type { Module } from '../../types';
import { cn } from '../../lib/utils';
import { MODULE_TYPE_COLORS, U_HEIGHT_PX, PORTS_PER_ROW } from '../../lib/constants';
import { ModuleEditPanel } from '../modals/ModuleEditPanel';
import { PortConfigModal } from '../modals/PortConfigModal';
import { useRackStore } from '../../store/useRackStore';
import { apiClient } from '../../api/client';

interface ModuleBlockProps {
  module: Module;
}

export function ModuleBlock({ module }: ModuleBlockProps) {
  const { racks, updateModuleLocal } = useRackStore();
  const [hovered, setHovered] = useState(false);
  const [editOpen, setEditOpen] = useState(false);
  const [portModalOpen, setPortModalOpen] = useState(false);
  const [selectedPortId, setSelectedPortId] = useState<string | null>(null);

  // Resize state
  const [previewUSize, setPreviewUSize] = useState<number | null>(null);
  const isResizing = useRef(false);
  const resizeStartY = useRef(0);
  const resizeStartUSize = useRef(0);

  const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
    id: `module-${module.id}`,
    disabled: isResizing.current,
    data: {
      dragType: 'module',
      moduleId: module.id,
      fromRackId: module.rackId,
      fromUPosition: module.uPosition,
      uSize: module.uSize,
      label: module.name,
    },
  });

  const colors = MODULE_TYPE_COLORS[module.type];
  const displayUSize = previewUSize ?? module.uSize;
  const height = displayUSize * U_HEIGHT_PX;
  const hasPorts = module.ports.length > 0;

  // Split ports into rows of PORTS_PER_ROW
  const portRows: (typeof module.ports)[] = [];
  for (let i = 0; i < module.ports.length; i += PORTS_PER_ROW) {
    portRows.push(module.ports.slice(i, i + PORTS_PER_ROW));
  }
  // Only show as many rows as fit within the current height
  // Each row needs ~14px (10px dot + 4px gap/padding)
  const availableForPorts = height - 16; // subtract top padding + resize handle
  const maxRows = Math.max(1, Math.floor(availableForPorts / 14));
  const visibleRows = portRows.slice(0, maxRows);
  const hiddenPortCount = module.ports.length - visibleRows.flat().length;

  // Compute the maximum allowed uSize for this module (rack bounds + collision)
  const maxResizeU = useCallback((): number => {
    const rack = racks.find((r) => r.id === module.rackId);
    if (!rack) return module.uSize;
    // Bound by rack totalU
    const rackMax = rack.totalU - module.uPosition + 1;
    // Find the first module that starts at uPosition >= module.uPosition + 1 (anything below us)
    const nextStart = rack.modules
      .filter((m) => m.id !== module.id && m.uPosition > module.uPosition)
      .reduce((min, m) => Math.min(min, m.uPosition), rack.totalU + 1);
    const collisionMax = nextStart - module.uPosition;
    return Math.min(rackMax, collisionMax);
  }, [racks, module]);

  function handleResizePointerDown(e: React.PointerEvent<HTMLDivElement>) {
    e.preventDefault();
    e.stopPropagation();
    isResizing.current = true;
    resizeStartY.current = e.clientY;
    resizeStartUSize.current = module.uSize;
    (e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);
  }

  function handleResizePointerMove(e: React.PointerEvent<HTMLDivElement>) {
    if (!isResizing.current) return;
    const deltaY = e.clientY - resizeStartY.current;
    const deltaU = Math.round(deltaY / U_HEIGHT_PX);
    const max = maxResizeU();
    const newU = Math.max(1, Math.min(resizeStartUSize.current + deltaU, max));
    setPreviewUSize(newU);
  }

  async function handleResizePointerUp(e: React.PointerEvent<HTMLDivElement>) {
    if (!isResizing.current) return;
    isResizing.current = false;
    (e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId);
    const finalU = previewUSize ?? module.uSize;
    setPreviewUSize(null);
    if (finalU === module.uSize) return;
    try {
      await apiClient.modules.update(module.id, { uSize: finalU });
      updateModuleLocal(module.id, { uSize: finalU });
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Resize failed');
    }
  }

  function openPort(portId: string) {
    setSelectedPortId(portId);
    setPortModalOpen(true);
  }

  const { cablingFromPortId, setCablingFromPortId, createConnection } = useRackStore();

  async function handlePortClick(e: React.MouseEvent, portId: string) {
    e.stopPropagation();

    // If shift key is pressed, open config modal as before
    if (e.shiftKey) {
      openPort(portId);
      return;
    }

    // Toggle cabling mode
    if (!cablingFromPortId) {
      setCablingFromPortId(portId);
    } else if (cablingFromPortId === portId) {
      setCablingFromPortId(null);
    } else {
      // Connect!
      try {
        await createConnection(cablingFromPortId, portId);
        setCablingFromPortId(null);
        toast.success('Patch cable connected');
      } catch (err) {
        toast.error(err instanceof Error ? err.message : 'Connection failed');
        setCablingFromPortId(null);
      }
    }
  }

  return (
    <>
      <div
        ref={setNodeRef}
        {...listeners}
        {...attributes}
        className={cn(
          'module-block relative w-full border-l-4 select-none overflow-hidden transition-opacity',
          colors.bg,
          colors.border,
          isDragging ? 'opacity-0 pointer-events-none' : 'cursor-grab active:cursor-grabbing',
          !isDragging && hovered && 'brightness-110',
          previewUSize !== null && 'ring-1 ring-white/30'
        )}
        style={{ height }}
        title={`${module.name}${module.ipAddress ? ` — ${module.ipAddress}` : ''}`}
        onMouseEnter={() => setHovered(true)}
        onMouseLeave={() => setHovered(false)}
        onClick={() => !isDragging && !isResizing.current && setEditOpen(true)}
        role="button"
        tabIndex={0}
        aria-label={`Edit ${module.name}`}
        onKeyDown={(e) => e.key === 'Enter' && setEditOpen(true)}
      >
        {/* Port grid — primary face content */}
        {hasPorts && previewUSize === null ? (
          <div className="flex flex-col gap-[3px] px-2 pt-[5px]">
            {visibleRows.map((row, rowIdx) => (
              <div key={rowIdx} className="flex gap-[3px]">
                {row.map((port) => {
                  const hasVlan = port.vlans.length > 0;
                  const vlanColor = hasVlan
                    ? port.mode === 'ACCESS'
                      ? port.vlans[0]?.vlan?.color || '#10b981'
                      : '#8b5cf6'
                    : '#475569';
                  const isCablingSource = cablingFromPortId === port.id;

                  return (
                    <button
                      key={port.id}
                      data-port-id={port.id}
                      onPointerDown={(e) => e.stopPropagation()}
                      onClick={(e) => handlePortClick(e, port.id)}
                      aria-label={`Port ${port.portNumber}`}
                      title={`Port ${port.portNumber}${port.label ? ` · ${port.label}` : ''}${
                        hasVlan ? ` (VLAN ${port.vlans.map((v) => v.vlan.vlanId).join(',')})` : ''
                      }\nShift+Click for settings`}
                      style={{ backgroundColor: vlanColor, borderColor: 'rgba(0,0,0,0.2)' }}
                      className={cn(
                        'w-2.5 h-2.5 rounded-sm border transition-all shrink-0 hover:scale-110 hover:brightness-125',
                        isCablingSource &&
                          'ring-2 ring-blue-400 ring-offset-1 ring-offset-slate-900 animate-pulse'
                      )}
                    />
                  );
                })}
              </div>
            ))}
            {hiddenPortCount > 0 && (
              <span className="text-[9px] text-white/40 leading-none">+{hiddenPortCount} more</span>
            )}
          </div>
        ) : (
          previewUSize === null && (
            <div className="px-2 pt-1.5 text-[10px] text-white/30 italic select-none">no ports</div>
          )
        )}

        {/* Resize preview label */}
        {previewUSize !== null && (
          <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
            <span className="text-xs font-bold text-white/80 bg-black/40 px-2 py-0.5 rounded">
              {previewUSize}U
            </span>
          </div>
        )}

        {/* Resize handle — bottom edge */}
        <div
          className={cn(
            'absolute bottom-0 left-0 right-0 h-3 flex items-center justify-center z-20',
            'cursor-ns-resize touch-none',
            hovered || previewUSize !== null ? 'opacity-100' : 'opacity-0 hover:opacity-100',
            'transition-opacity'
          )}
          onPointerDown={(e) => { e.stopPropagation(); handleResizePointerDown(e); }}
          onPointerMove={handleResizePointerMove}
          onPointerUp={handleResizePointerUp}
          onClick={(e) => e.stopPropagation()}
          aria-label="Resize module"
          title="Drag to resize"
        >
          <GripHorizontal size={9} className="text-white/40 pointer-events-none" />
        </div>
      </div>

      <ModuleEditPanel module={module} open={editOpen} onClose={() => setEditOpen(false)} />

      {selectedPortId && (
        <PortConfigModal
          portId={selectedPortId}
          open={portModalOpen}
          onClose={() => {
            setPortModalOpen(false);
            setSelectedPortId(null);
          }}
        />
      )}
    </>
  );
}
