import { Component, For, Show, createSignal, createMemo, createEffect } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import { useWebSocket } from '@/App';
import { getAlertStyles } from '@/utils/alerts';
import { formatBytes, formatPercent } from '@/utils/format';
import type { Storage as StorageType, CephCluster } from '@/types/api';
import { ComponentErrorBoundary } from '@/components/ErrorBoundary';
import { UnifiedNodeSelector } from '@/components/shared/UnifiedNodeSelector';
import { StorageFilter } from './StorageFilter';
import { DiskList } from './DiskList';
import { Card } from '@/components/shared/Card';
import { EmptyState } from '@/components/shared/EmptyState';
import { NodeGroupHeader } from '@/components/shared/NodeGroupHeader';
import { ProxmoxSectionNav } from '@/components/Proxmox/ProxmoxSectionNav';
import { getNodeDisplayName } from '@/utils/nodes';
import { usePersistentSignal } from '@/hooks/usePersistentSignal';
import { useAlertsActivation } from '@/stores/alertsActivation';

type StorageSortKey = 'name' | 'node' | 'type' | 'status' | 'usage' | 'free' | 'total';

const Storage: Component = () => {
  const navigate = useNavigate();
  const { state, connected, activeAlerts, initialDataReceived } = useWebSocket();
  const alertsActivation = useAlertsActivation();
  const alertsEnabled = createMemo(() => alertsActivation.activationState() === 'active');
  const [viewMode, setViewMode] = usePersistentSignal<'node' | 'storage'>(
    'storageViewMode',
    'node',
    {
      deserialize: (raw) => (raw === 'storage' ? 'storage' : 'node'),
    },
  );
  const [tabView, setTabView] = createSignal<'pools' | 'disks'>('pools');
  const [searchTerm, setSearchTerm] = createSignal('');
  const [selectedNode, setSelectedNode] = createSignal<string | null>(null);
  const [expandedStorage, setExpandedStorage] = createSignal<string | null>(null);
  const [sortKey, setSortKey] = usePersistentSignal<StorageSortKey>('storageSortKey', 'name', {
    deserialize: (raw) =>
      (['name', 'node', 'type', 'status', 'usage', 'free', 'total'] as const).includes(
        raw as StorageSortKey,
      )
        ? (raw as StorageSortKey)
        : 'name',
  });
  const [sortDirection, setSortDirection] = usePersistentSignal<'asc' | 'desc'>(
    'storageSortDirection',
    'asc',
    {
      deserialize: (raw) => (raw === 'desc' ? 'desc' : 'asc'),
    },
  );

  // Create a mapping from node instance ID to node object
  const nodeByInstance = createMemo(() => {
    const map: Record<string, typeof state.nodes[0]> = {};
    (state.nodes || []).forEach((node) => {
      map[node.id] = node;
    });
    return map;
  });

  const isCephType = (type?: string) => {
    const value = (type || '').toLowerCase();
    return value === 'rbd' || value === 'cephfs' || value === 'ceph';
  };

  const getCephHealthLabel = (health?: string) => {
    if (!health) return 'CEPH';
    const normalized = health.toUpperCase();
    return normalized.startsWith('HEALTH_') ? normalized.replace('HEALTH_', '') : normalized;
  };

  const getCephHealthStyles = (health?: string) => {
    const normalized = (health || '').toUpperCase();
    if (normalized === 'HEALTH_OK') {
      return 'bg-green-100 text-green-700 dark:bg-green-900/60 dark:text-green-300 border border-green-200 dark:border-green-800';
    }
    if (normalized === 'HEALTH_WARN' || normalized === 'HEALTH_WARNING') {
      return 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/60 dark:text-yellow-200 border border-yellow-300 dark:border-yellow-800';
    }
    if (normalized === 'HEALTH_ERR' || normalized === 'HEALTH_ERROR' || normalized === 'HEALTH_CRIT') {
      return 'bg-red-100 text-red-700 dark:bg-red-900/60 dark:text-red-200 border border-red-300 dark:border-red-800';
    }
    return 'bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-200 border border-blue-200 dark:border-blue-700';
  };

  const cephClusters = createMemo(() => state.cephClusters || []);

  const visibleCephClusters = createMemo<CephCluster[]>(() => {
    const explicit = cephClusters();
    if (explicit && explicit.length > 0) {
      return explicit;
    }

    const storageList = state.storage || [];
    const summaryByInstance = new Map<
      string,
      {
        total: number;
        used: number;
        nodes: Set<string>;
        storages: number;
      }
    >();

    storageList.forEach((item) => {
      if (!isCephType(item?.type)) {
        return;
      }
      const instance = item.instance || 'ceph';
      let summary = summaryByInstance.get(instance);
      if (!summary) {
        summary = {
          total: 0,
          used: 0,
          nodes: new Set<string>(),
          storages: 0,
        };
        summaryByInstance.set(instance, summary);
      }

      summary.total += Math.max(0, item.total || 0);
      summary.used += Math.max(0, item.used || 0);
      summary.storages += 1;

      if (Array.isArray(item.nodes)) {
        item.nodes.forEach((node) => node && summary!.nodes.add(node));
      } else if (item.node) {
        summary.nodes.add(item.node);
      }
    });

    return Array.from(summaryByInstance.entries()).map(([instance, info], index) => {
      const totalBytes = info.total;
      const usedBytes = info.used;
      const availableBytes = Math.max(totalBytes - usedBytes, 0);
      const usagePercent = totalBytes > 0 ? (usedBytes / totalBytes) * 100 : 0;
      const numOsds = Math.max(1, info.storages * 2);
      const numMons = Math.min(3, Math.max(1, info.nodes.size));

      return {
        id: `${instance}-derived-${index}`,
        instance,
        name: `${instance} Ceph`,
        health: 'HEALTH_UNKNOWN',
        healthMessage: 'Derived from storage metrics – live Ceph telemetry unavailable.',
        totalBytes,
        usedBytes,
        availableBytes,
        usagePercent,
        numMons,
        numMgrs: numMons > 1 ? 2 : 1,
        numOsds,
        numOsdsUp: numOsds,
        numOsdsIn: numOsds,
        numPGs: Math.max(128, info.storages * 128),
        pools: undefined,
        services: undefined,
        lastUpdated: Date.now(),
      } as CephCluster;
    });
  });

  const cephClusterByInstance = createMemo<Record<string, CephCluster>>(() => {
    const map: Record<string, CephCluster> = {};
    visibleCephClusters().forEach((cluster) => {
      if (cluster?.instance) {
        map[cluster.instance] = cluster;
      }
    });
    return map;
  });


  const sortKeyOptions: { value: StorageSortKey; label: string }[] = [
    { value: 'name', label: 'Name' },
    { value: 'node', label: 'Node' },
    { value: 'type', label: 'Type' },
    { value: 'status', label: 'Status' },
    { value: 'usage', label: 'Usage %' },
    { value: 'free', label: 'Free Capacity' },
    { value: 'total', label: 'Total Capacity' },
  ];

  // Filter storage - in storage view, filter out 0 capacity and deduplicate
  const filteredStorage = createMemo(() => {
    let storage = state.storage || [];

    // In storage view, deduplicate identical storage and filter out 0 capacity
    if (viewMode() === 'storage') {
      // Filter out 0 capacity first
      storage = storage.filter((s) => s.total > 0);

      // Deduplicate storage entries that are identical
      const storageMap = new Map();
      storage.forEach((s) => {
        let key;
        const nodeId = `${s.instance}-${s.node}`;

        // For PBS storage, group by capacity since they're the same PBS server
        // PBS namespaces (pbs-node1, pbs-node2) pointing to same server should be grouped
        if (s.type === 'pbs' && s.shared) {
          // Group shared PBS by type, total size, and usage (same PBS server = same capacity)
          key = `pbs-${s.total}-${s.used}`;
        } else if (s.shared) {
          // Other shared storage should only appear once
          key = `${s.name}-${s.type}`;
        } else {
          // Non-shared storage (local, local-zfs) - NEVER deduplicate these!
          // Each node has its own physical storage
          key = `${s.node}-${s.name}-${s.type}`;
        }

        if (!storageMap.has(key)) {
          const backendNodes = (s.nodes ?? []).filter((node): node is string => Boolean(node));
          const backendNodeIds = ((s as { nodeIds?: string[] }).nodeIds ?? []).filter(
            (id): id is string => Boolean(id),
          );

          const rawNodes = backendNodes.length > 0 ? backendNodes : [s.node].filter(Boolean);
          const uniqueNodes = Array.from(new Set(rawNodes));
          const normalizedInitialNodes =
            uniqueNodes.length > 1 ? uniqueNodes.filter((node) => node !== 'cluster') : uniqueNodes;
          const nodesForStorage =
            normalizedInitialNodes.length > 0 ? normalizedInitialNodes : uniqueNodes;
          const initialNodeIds = Array.from(
            new Set(backendNodeIds.length > 0 ? backendNodeIds : [nodeId].filter(Boolean)),
          );

          const initialPBSNames =
            s.type === 'pbs'
              ? Array.from(new Set((s.pbsNames ?? [s.name]).filter((name): name is string => Boolean(name))))
              : undefined;

          // First occurrence - store it with node list
          storageMap.set(key, {
            ...s,
            name: s.type === 'pbs' ? 'PBS Storage' : s.name, // Generic name for PBS
            nodes: nodesForStorage,
            nodeIds: initialNodeIds,
            nodeCount: nodesForStorage.length,
            pbsNames: initialPBSNames, // Track individual PBS names
          });
        } else {
          // Duplicate - just add to node list
          const existing = storageMap.get(key);

          const backendNodes = (s.nodes ?? []).filter((node): node is string => Boolean(node));
          const rawCandidateNodes =
            backendNodes.length > 0 ? backendNodes : [s.node].filter(Boolean);
          const candidateNodes =
            rawCandidateNodes.length > 1
              ? rawCandidateNodes.filter((node) => node !== 'cluster')
              : rawCandidateNodes;
          candidateNodes.forEach((node) => {
            if (!existing.nodes.includes(node)) {
              existing.nodes.push(node);
            }
          });
          existing.nodeCount = existing.nodes.length;

          const backendNodeIds = ((s as { nodeIds?: string[] }).nodeIds ?? []).filter(
            (id): id is string => Boolean(id),
          );
          const candidateNodeIds =
            backendNodeIds.length > 0 ? backendNodeIds : [nodeId].filter(Boolean);

          if (!existing.nodeIds) {
            existing.nodeIds = [];
          }
          candidateNodeIds.forEach((id) => {
            if (!existing.nodeIds!.includes(id)) {
              existing.nodeIds!.push(id);
            }
          });

          // For PBS, collect all namespace names (merging backend-provided data)
          if (s.type === 'pbs') {
            if (!existing.pbsNames) {
              existing.pbsNames = [];
            }
            const incomingPBSNames = (s.pbsNames ?? [s.name]).filter(
              (name): name is string => Boolean(name),
            );
            incomingPBSNames.forEach((name) => {
              if (!existing.pbsNames!.includes(name)) {
                existing.pbsNames!.push(name);
              }
            });
          }
        }
      });

      // Convert back to array
      storage = Array.from(storageMap.values());
    }

    return storage;
  });

  // Sort and filter storage
  const sortedStorage = createMemo(() => {
    let storage = [...filteredStorage()];

    // Apply node selection filter with instance-aware matching
    const nodeFilter = selectedNode();
    if (nodeFilter) {
      const node = state.nodes?.find((n) => n.id === nodeFilter);
      if (node) {
        const nodeId = `${node.instance}-${node.name}`;
        storage = storage.filter((s) => {
          const belongsToNode = s.instance === node.instance && s.node === node.name;
          const aggregatedNodeIds = (s as { nodeIds?: string[] }).nodeIds ?? [];
          return belongsToNode || aggregatedNodeIds.includes(nodeId);
        });
      }
    }

    // Apply search filter
    let search = searchTerm().toLowerCase().trim();
    if (search) {
      const nodePattern = /node:([a-z0-9_.:-]+)/i;
      const nodeMatch = search.match(nodePattern);
      let nodeQuery: string | null = null;

      if (nodeMatch) {
        nodeQuery = nodeMatch[1].toLowerCase();
        search = search.replace(nodeMatch[0], '').trim();
      }

      if (nodeQuery) {
        storage = storage.filter((s) => {
          const primary = s.node?.toLowerCase();
          const extraNodes = s.nodes?.map((node) => node.toLowerCase()) || [];
          return primary === nodeQuery || extraNodes.includes(nodeQuery);
        });
      }

      if (search) {
        const terms = search.split(/\s+/).filter(Boolean);
        storage = storage.filter((s) => {
          const haystack = [
            s.name,
            s.node,
            s.type,
            s.content,
            s.status,
            ...(s.nodes ?? []),
            ...(s.pbsNames ?? []),
          ]
            .filter(Boolean)
            .map((value) => value!.toLowerCase());

          return terms.every((term) => haystack.some((entry) => entry.includes(term)));
        });
      }
    }

    const numericCompare = (a: number, b: number) => {
      const normalizedA = Number.isFinite(a) ? a : -Infinity;
      const normalizedB = Number.isFinite(b) ? b : -Infinity;
      if (normalizedA === normalizedB) return 0;
      return normalizedA < normalizedB ? -1 : 1;
    };

    const result = storage.sort((a, b) => {
      let comparison = 0;

      switch (sortKey()) {
        case 'node': {
          const nodeA = (a.nodes && a.nodes.length > 0 ? a.nodes[0] : a.node) ?? '';
          const nodeB = (b.nodes && b.nodes.length > 0 ? b.nodes[0] : b.node) ?? '';
          comparison = nodeA.localeCompare(nodeB, undefined, { sensitivity: 'base' });
          break;
        }
        case 'type':
          comparison = (a.type ?? '').localeCompare(b.type ?? '', undefined, {
            sensitivity: 'base',
          });
          break;
        case 'status':
          comparison = (a.status ?? '').localeCompare(b.status ?? '', undefined, {
            sensitivity: 'base',
          });
          break;
        case 'usage':
          comparison = numericCompare(a.usage ?? 0, b.usage ?? 0);
          break;
        case 'free':
          comparison = numericCompare(a.free ?? 0, b.free ?? 0);
          break;
        case 'total':
          comparison = numericCompare(a.total ?? 0, b.total ?? 0);
          break;
        case 'name':
        default:
          comparison = (a.name ?? '').localeCompare(b.name ?? '', undefined, {
            sensitivity: 'base',
          });
          break;
      }

      if (comparison === 0) {
        comparison = (a.name ?? '').localeCompare(b.name ?? '', undefined, { sensitivity: 'base' });
      }

      return sortDirection() === 'asc' ? comparison : -comparison;
    });

    return result;
  });

  // Group storage by node or storage
  const groupedStorage = createMemo(() => {
    const storage = sortedStorage();
    const mode = viewMode();

    if (mode === 'node') {
      // Group by node ID (instance + node name) to match Node.ID format
      const groups: Record<string, StorageType[]> = {};
      storage.forEach((s) => {
        if (s.shared) {
          const nodeCandidates = [
            ...((s.nodeIds ?? [])
              .map((id) => (id.startsWith(`${s.instance}-`) ? id.slice(s.instance.length + 1) : id))
              .filter((node): node is string => Boolean(node))),
            ...((s.nodes ?? []).filter((node): node is string => Boolean(node))),
          ];
          const normalizedNodes = Array.from(
            new Set(
              nodeCandidates
                .map((node) => node.trim())
                .filter((node) => node && node !== 'shared' && node !== 'cluster'),
            ),
          );
          const nodesForStorage =
            normalizedNodes.length > 0
              ? normalizedNodes
              : [s.node].map((node) => node?.trim()).filter((node): node is string => Boolean(node));

          nodesForStorage.forEach((nodeName) => {
            const key = `${s.instance}-${nodeName}`;
            if (!groups[key]) groups[key] = [];
            groups[key].push({ ...s, node: nodeName });
          });
          return;
        }

        const key = `${s.instance}-${s.node}`;
        if (!groups[key]) groups[key] = [];
        groups[key].push(s);
      });
      return groups;
    } else {
      // Group by storage name - show all storage as-is for maximum compatibility
      const groups: Record<string, StorageType[]> = {};

      storage.forEach((s) => {
        if (!groups[s.name]) groups[s.name] = [];
        groups[s.name].push(s);
      });

      return groups;
    }
  });

  const getProgressBarColor = (usage: number) => {
    // Match MetricBar component styling exactly - disk type thresholds
    if (usage >= 90) return 'bg-red-500/60 dark:bg-red-500/50';
    if (usage >= 80) return 'bg-yellow-500/60 dark:bg-yellow-500/50';
    return 'bg-green-500/60 dark:bg-green-500/50';
  };

  const resetFilters = () => {
    setSearchTerm('');
    setSelectedNode(null);
    setViewMode('node');
    setSortKey('name');
    setSortDirection('asc');
  };

  // Handle keyboard shortcuts
  let searchInputRef: HTMLInputElement | undefined;

  createEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      // Ignore if user is typing in an input
      const target = e.target as HTMLElement;
      const isInputField =
        target.tagName === 'INPUT' ||
        target.tagName === 'TEXTAREA' ||
        target.tagName === 'SELECT' ||
        target.contentEditable === 'true';

      // Escape key behavior
      if (e.key === 'Escape') {
        // Clear search and reset filters
        if (searchTerm().trim() || selectedNode() || viewMode() !== 'node') {
          resetFilters();

          // Blur the search input if it's focused
          if (searchInputRef && document.activeElement === searchInputRef) {
            searchInputRef.blur();
          }
        }
      } else if (!isInputField && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
        // If it's a printable character and user is not in an input field
        // Focus the search input and let the character be typed
        if (searchInputRef) {
          searchInputRef.focus();
        }
      }
    };

    document.addEventListener('keydown', handleKeyDown);
    return () => document.removeEventListener('keydown', handleKeyDown);
  });

  const handleNodeSelect = (nodeId: string | null) => {
    setSelectedNode(nodeId);
  };

  return (
    <div class="space-y-3">
      <ProxmoxSectionNav current="storage" />

      {/* Node Selector */}
      <UnifiedNodeSelector
        currentTab="storage"
        globalTemperatureMonitoringEnabled={state.temperatureMonitoringEnabled}
        onNodeSelect={handleNodeSelect}
        filteredStorage={sortedStorage()}
        searchTerm={searchTerm()}
      />

      {/* Tab Toggle */}
      <div class="mb-4">
        <nav class="flex items-center gap-4" aria-label="Storage tabs">
          <button
            onClick={() => setTabView('pools')}
            type="button"
            class={`inline-flex items-center px-2 sm:px-3 py-1 text-sm font-medium border-b-2 border-transparent text-gray-600 dark:text-gray-400 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400/60 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-gray-900 ${
              tabView() === 'pools'
                ? 'text-blue-600 dark:text-blue-300 border-blue-500 dark:border-blue-400'
                : 'hover:text-blue-500 dark:hover:text-blue-300 hover:border-blue-300/70 dark:hover:border-blue-500/50'
            }`}
          >
            Storage Pools
          </button>
          <button
            onClick={() => setTabView('disks')}
            type="button"
            class={`inline-flex items-center px-2 sm:px-3 py-1 text-sm font-medium border-b-2 border-transparent text-gray-600 dark:text-gray-400 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400/60 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-gray-900 ${
              tabView() === 'disks'
                ? 'text-blue-600 dark:text-blue-300 border-blue-500 dark:border-blue-400'
                : 'hover:text-blue-500 dark:hover:text-blue-300 hover:border-blue-300/70 dark:hover:border-blue-500/50'
            }`}
          >
            Physical Disks
          </button>
        </nav>
      </div>

      {/* Show Storage Filter only for pools */}
      <Show when={tabView() === 'pools'}>
        <StorageFilter
          search={searchTerm}
          setSearch={setSearchTerm}
          groupBy={viewMode}
          setGroupBy={setViewMode}
          sortOptions={sortKeyOptions}
          sortKey={sortKey}
          setSortKey={setSortKey}
          sortDirection={sortDirection}
          setSortDirection={setSortDirection}
          searchInputRef={(el) => (searchInputRef = el)}
        />
      </Show>

      {/* Show simple search for disks */}
      <Show when={tabView() === 'disks'}>
        <Card class="mb-3" padding="sm">
          <div class="relative">
            <input
              type="text"
              placeholder="Search disks by model, path, or serial..."
              value={searchTerm()}
              onInput={(e) => setSearchTerm(e.currentTarget.value)}
              ref={(el) => (searchInputRef = el)}
              class="w-full pl-9 pr-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg 
                     bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-500
                     focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 dark:focus:border-blue-400 outline-none transition-all"
            />
            <svg
              class="absolute left-3 top-2 h-4 w-4 text-gray-400 dark:text-gray-500"
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
            >
              <path
                stroke-linecap="round"
                stroke-linejoin="round"
                stroke-width="2"
                d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
              />
            </svg>
          </div>
        </Card>
      </Show>

      {/* Loading State */}
      <Show when={connected() && !initialDataReceived()}>
        <Card padding="lg">
          <EmptyState
            icon={
              <div class="mx-auto flex h-12 w-12 items-center justify-center">
                <svg class="h-8 w-8 animate-spin text-gray-400" fill="none" viewBox="0 0 24 24">
                  <circle
                    class="opacity-25"
                    cx="12"
                    cy="12"
                    r="10"
                    stroke="currentColor"
                    stroke-width="4"
                  />
                  <path
                    class="opacity-75"
                    fill="currentColor"
                    d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
                  />
                </svg>
              </div>
            }
            title="Loading storage data..."
            description="Connecting to monitoring service"
          />
        </Card>
      </Show>

      {/* Helpful hint for no PVE nodes but still show content */}
      <Show
        when={
          connected() &&
          initialDataReceived() &&
          (state.nodes || []).filter((n) => n.type === 'pve').length === 0 &&
          sortedStorage().length === 0 &&
          searchTerm().trim() === ''
        }
      >
        <Card padding="lg">
          <EmptyState
            icon={
              <svg
                class="h-12 w-12 text-gray-400"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
              >
                <path
                  stroke-linecap="round"
                  stroke-linejoin="round"
                  stroke-width="2"
                  d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
                />
              </svg>
            }
            title="No storage configured"
            description="Add a Proxmox VE or PBS node in the Settings tab to start monitoring storage."
            actions={
              <button
                type="button"
                onClick={() => navigate('/settings')}
                class="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
              >
                Go to Settings
              </button>
            }
          />
        </Card>
      </Show>

      {/* Conditional rendering based on tab */}
      <Show when={tabView() === 'pools'}>
        {/* No results found message for storage pools */}
        <Show
          when={
            connected() &&
            initialDataReceived() &&
            sortedStorage().length === 0 &&
            searchTerm().trim() !== ''
          }
        >
          <Card padding="lg">
            <EmptyState
              icon={
                <svg
                  class="h-12 w-12 text-gray-400"
                  fill="none"
                  viewBox="0 0 24 24"
                  stroke="currentColor"
                >
                  <path
                    stroke-linecap="round"
                    stroke-linejoin="round"
                    stroke-width="2"
                    d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
                  />
                </svg>
              }
              title="No storage found"
              description={`No storage matches your search "${searchTerm()}"`}
            />
          </Card>
        </Show>

        {/* Storage Table - shows for both PVE and PBS storage */}
        <Show when={connected() && initialDataReceived() && sortedStorage().length > 0}>
          <ComponentErrorBoundary name="Storage Table">
            <Card padding="none" class="mb-4 overflow-hidden">
              <div class="overflow-x-auto" style="scrollbar-width: none; -ms-overflow-style: none;">
                <style>{`
                .overflow-x-auto::-webkit-scrollbar { display: none; }
              `}</style>
                <table class="w-full">
                  <thead>
                    <tr class="bg-gray-50 dark:bg-gray-700/50 text-gray-600 dark:text-gray-300 border-b border-gray-200 dark:border-gray-600">
                      <th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider">
                        Storage
                      </th>
                      <th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider hidden md:table-cell">
                        Type
                      </th>
                      <th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider hidden lg:table-cell">
                        Content
                      </th>
                      <th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider hidden sm:table-cell">
                        Status
                      </th>
                      <Show when={viewMode() === 'node'}>
                        <th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider hidden lg:table-cell">
                          Shared
                        </th>
                      </Show>
                      <th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider min-w-[200px]">
                        Usage
                      </th>
                      <th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider hidden sm:table-cell">
                        Free
                      </th>
                      <th class="px-2 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider">
                        Total
                      </th>
                      <th class="px-2 py-1.5 w-8"></th>
                    </tr>
                  </thead>
                  <tbody class="divide-y divide-gray-200 dark:divide-gray-700">
                    <For
                      each={Object.entries(groupedStorage()).sort(([instanceIdA], [instanceIdB]) => {
                        // Sort by friendly node name for display when in node mode
                        if (viewMode() === 'node') {
                          const nodeA = nodeByInstance()[instanceIdA];
                          const nodeB = nodeByInstance()[instanceIdB];
                          const labelA = nodeA ? getNodeDisplayName(nodeA) : instanceIdA;
                          const labelB = nodeB ? getNodeDisplayName(nodeB) : instanceIdB;
                          return labelA.localeCompare(labelB);
                        }
                        // Sort by storage name when in storage mode
                        return instanceIdA.localeCompare(instanceIdB);
                      })}
                    >
                      {([groupKey, storages]) => {
                        const node = viewMode() === 'node' ? nodeByInstance()[groupKey] : null;
                        return (
                          <>
                            {/* Group Header */}
                            <Show when={viewMode() === 'node' && node}>
                              {(validNode) => <NodeGroupHeader node={validNode()} colspan={9} />}
                            </Show>

                          {/* Storage Rows */}
                          <For each={storages} fallback={<></>}>
                            {(storage) => {
                              const usagePercent =
                                storage.total > 0 ? (storage.used / storage.total) * 100 : 0;
                              const isDisabled = storage.status !== 'available';
                              const pbsNamesDisplay = createMemo(() => {
                                const names = storage.pbsNames?.filter(
                                  (name): name is string => Boolean(name),
                                );
                                if (!names || names.length === 0) return '';
                                return [...names].sort((a, b) => a.localeCompare(b)).join(', ');
                              });

                              const nodeListDisplay = createMemo(() => {
                                const nodes =
                                  storage.nodes && storage.nodes.length > 0
                                    ? storage.nodes.filter((node): node is string => Boolean(node))
                                    : [storage.node].filter((node): node is string => Boolean(node));
                                return nodes.join(', ');
                              });

                              const alertStyles = createMemo(() =>
                                getAlertStyles(
                                  storage.id || `${storage.instance}-${storage.node}-${storage.name}`,
                                  activeAlerts,
                                  alertsEnabled(),
                                ),
                              );

                              const parentNodeOnline = createMemo(() => {
                                if (viewMode() === 'node' && node) {
                                  return node.status === 'online' && (node.uptime || 0) > 0;
                                }
                                return true;
                              });

                              const showAlertHighlight = createMemo(
                                () => alertStyles().hasUnacknowledgedAlert && parentNodeOnline(),
                              );

                              const isCephStorage = createMemo(() => isCephType(storage.type));
                              const canExpand = createMemo(() => isCephStorage());
                              const zfsPool = storage.zfsPool;
                              const storageRowId = createMemo(
                                () => storage.id || `${storage.instance}-${storage.node}-${storage.name}`,
                              );
                              const cephCluster = createMemo(
                                () => cephClusterByInstance()[storage.instance],
                              );
                              const cephInstanceStorages = createMemo(() =>
                                (state.storage || []).filter(
                                  (item) =>
                                    item.instance === storage.instance && isCephType(item.type),
                                ),
                              );
                              const cephHealthLabel = createMemo(() => {
                                const health = (cephCluster()?.health || '').toUpperCase();
                                if (!health || health === 'HEALTH_UNKNOWN') {
                                  return '';
                                }
                                return getCephHealthLabel(health);
                              });
                              const cephHealthClass = createMemo(() => {
                                const health = (cephCluster()?.health || '').toUpperCase();
                                if (!health || health === 'HEALTH_UNKNOWN') {
                                  return '';
                                }
                                return getCephHealthStyles(health);
                              });
                              const drawerDisabled = createMemo(
                                () => isDisabled || !parentNodeOnline(),
                              );
                              const cephSummaryText = createMemo(() => {
                                const cluster = cephCluster();
                                const parts: string[] = [];

                                if (cluster && Number.isFinite(cluster.totalBytes)) {
                                  const total = Math.max(0, cluster.totalBytes || 0);
                                  const used = Math.max(0, cluster.usedBytes || 0);
                                  const percent = total > 0 ? (used / total) * 100 : 0;
                                  parts.push(
                                    `${formatBytes(used, 0)} / ${formatBytes(total, 0)} (${formatPercent(percent)})`,
                                  );
                                  if (
                                    Number.isFinite(cluster.numOsds) &&
                                    Number.isFinite(cluster.numOsdsUp)
                                  ) {
                                    parts.push(`OSDs ${cluster.numOsdsUp}/${cluster.numOsds}`);
                                  }
                                  if (Number.isFinite(cluster.numPGs) && cluster.numPGs > 0) {
                                    parts.push(`PGs ${cluster.numPGs.toLocaleString()}`);
                                  }
                                } else {
                                  const storages = cephInstanceStorages();
                                  if (storages.length > 0) {
                                    const totals = storages.reduce(
                                      (acc, item) => {
                                        acc.total += Math.max(0, item.total || 0);
                                        acc.used += Math.max(0, item.used || 0);
                                        return acc;
                                      },
                                      { total: 0, used: 0 },
                                    );
                                    if (totals.total > 0) {
                                      const percent = (totals.used / totals.total) * 100;
                                      parts.push(
                                        `${formatBytes(totals.used, 0)} / ${formatBytes(totals.total, 0)} (${formatPercent(percent)})`,
                                      );
                                    }
                                  }
                                }

                                return parts.join(' • ');
                              });
                              const cephPoolsText = createMemo(() => {
                                const cluster = cephCluster();
                                if (cluster && cluster.pools && cluster.pools.length > 0) {
                                  return cluster.pools
                                    .slice(0, 2)
                                    .map((pool) => {
                                      if (!pool) return '';
                                      const total = Math.max(1, pool.storedBytes + pool.availableBytes);
                                      const percent = total > 0 ? (pool.storedBytes / total) * 100 : 0;
                                      return `${pool.name}: ${formatPercent(percent)}`;
                                    })
                                    .filter(Boolean)
                                    .join(', ');
                                }

                                const storages = cephInstanceStorages();
                                if (storages.length === 0) {
                                  return '';
                                }

                                return storages
                                  .slice(0, 2)
                                  .map((item) => {
                                    const total = Math.max(1, item.total || 0);
                                    const used = Math.max(0, item.used || 0);
                                    const percent = total > 0 ? (used / total) * 100 : 0;
                                    return `${item.name}: ${formatPercent(percent)}`;
                                  })
                                  .filter(Boolean)
                                  .join(', ');
                              });
                              const cephHealthMessage = createMemo(() => {
                                const cluster = cephCluster();
                                if (cluster?.healthMessage) {
                                  return cluster.healthMessage;
                                }
                                if (cluster && cluster.health && cluster.health !== 'HEALTH_UNKNOWN') {
                                  return '';
                                }
                                if (cephInstanceStorages().length > 0) {
                                  return 'Derived from storage metrics – live Ceph telemetry unavailable.';
                                }
                                return '';
                              });
                              const isExpanded = createMemo(
                                () => expandedStorage() === storageRowId(),
                              );

                              const hasAcknowledgedOnlyAlert = createMemo(
                                () => alertStyles().hasAcknowledgedOnlyAlert && parentNodeOnline(),
                              );

                              const rowClass = createMemo(() => {
                                const classes = [
                                  'transition-all duration-200',
                                  'hover:bg-gray-50 dark:hover:bg-gray-700/30',
                                  'hover:shadow-sm',
                                ];

                                if (showAlertHighlight()) {
                                  classes.push(
                                    alertStyles().severity === 'critical'
                                      ? 'bg-red-50 dark:bg-red-950/30'
                                      : 'bg-yellow-50 dark:bg-yellow-950/20',
                                  );
                                } else if (hasAcknowledgedOnlyAlert()) {
                                  classes.push('bg-gray-50/40 dark:bg-gray-800/40');
                                }

                                if (isDisabled || !parentNodeOnline()) {
                                  classes.push('opacity-60');
                                }

                                if (canExpand()) {
                                  classes.push('cursor-pointer');
                                }

                                if (canExpand() && isExpanded()) {
                                  classes.push('bg-gray-50 dark:bg-gray-800/40');
                                }

                                return classes.join(' ');
                              });

                              const rowStyle = createMemo(() => {
                                if (showAlertHighlight()) {
                                  const color =
                                    alertStyles().severity === 'critical' ? '#ef4444' : '#eab308';
                                  return {
                                    'box-shadow': `inset 4px 0 0 0 ${color}`,
                                  };
                                }
                                if (hasAcknowledgedOnlyAlert()) {
                                  return {
                                    'box-shadow': 'inset 4px 0 0 0 rgba(156, 163, 175, 0.8)',
                                  };
                                }
                                return {} as Record<string, string>;
                              });

                              const firstCellHasIndicator = createMemo(
                                () => showAlertHighlight() || hasAcknowledgedOnlyAlert(),
                              );

                              const firstCellClass = createMemo(() => {
                                if (viewMode() === 'node') {
                                  return firstCellHasIndicator()
                                    ? 'p-0.5 pl-7 pr-1.5'
                                    : 'p-0.5 pl-8 pr-1.5';
                                }
                                return firstCellHasIndicator()
                                  ? 'p-0.5 pl-3 pr-1.5'
                                  : 'p-0.5 pl-3 pr-1.5';
                              });

                              const toggleDrawer = () => {
                                if (!canExpand()) return;
                                setExpandedStorage((prev) =>
                                  prev === storageRowId() ? null : storageRowId(),
                                );
                              };

                              return (
                                <>
                                  <tr
                                    class={`${rowClass()} transition-colors`}
                                    style={rowStyle()}
                                    onClick={toggleDrawer}
                                    aria-expanded={canExpand() && isExpanded() ? 'true' : 'false'}
                                  >
                                    <td class={`${firstCellClass()} align-middle`}>
                                      <div class="flex items-center gap-2 min-w-0">
                                        <span
                                          class={`text-sm font-medium text-gray-900 dark:text-gray-100 truncate ${
                                            canExpand() ? 'max-w-[180px]' : 'max-w-[200px]'
                                          }`}
                                          title={storage.name}
                                        >
                                          {storage.name}
                                        </span>
                                        {/* ZFS Health Badge */}
                                        <Show when={zfsPool && zfsPool.state !== 'ONLINE'}>
                                          <span
                                            class={`px-1.5 py-0.5 rounded text-[10px] font-medium ${
                                              zfsPool?.state === 'DEGRADED'
                                                ? 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900 dark:text-yellow-300'
                                                : 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300'
                                            }`}
                                          >
                                            {zfsPool?.state}
                                          </span>
                                        </Show>
                                        {/* ZFS Error Badge */}
                                        <Show
                                          when={
                                            zfsPool &&
                                            (zfsPool.readErrors > 0 ||
                                              zfsPool.writeErrors > 0 ||
                                              zfsPool.checksumErrors > 0)
                                          }
                                        >
                                          <span class="px-1.5 py-0.5 rounded text-[10px] font-medium bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300">
                                            ERRORS
                                          </span>
                                        </Show>
                                        <Show when={viewMode() === 'storage'}>
                                          <Show when={storage.pbsNames}>
                                            <span
                                              class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap truncate max-w-[240px]"
                                              title={pbsNamesDisplay()}
                                            >
                                              ({pbsNamesDisplay()})
                                            </span>
                                          </Show>
                                          <Show when={!storage.pbsNames}>
                                            <span
                                              class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap truncate max-w-[240px]"
                                              title={nodeListDisplay()}
                                            >
                                              ({nodeListDisplay()})
                                            </span>
                                          </Show>
                                        </Show>
                                        <Show when={isCephStorage() && cephHealthLabel()}>
                                          <span
                                            class={`px-1.5 py-0.5 rounded text-[10px] font-medium ${cephHealthClass()}`}
                                            title={cephCluster()?.healthMessage}
                                          >
                                            {cephHealthLabel()}
                                          </span>
                                        </Show>
                                      </div>
                                    </td>
                                    <td class="p-0.5 px-1.5 hidden md:table-cell">
                                      <span class="inline-block px-1.5 py-0.5 text-[10px] font-medium rounded bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300">
                                        {storage.type}
                                      </span>
                                    </td>
                                    <td class="p-0.5 px-1.5 hidden lg:table-cell">
                                      <span
                                        class="text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap truncate max-w-[220px]"
                                        title={storage.content || '-'}
                                      >
                                        {storage.content || '-'}
                                      </span>
                                    </td>
                                    <td class="p-0.5 px-1.5 text-xs hidden sm:table-cell whitespace-nowrap">
                                      <span
                                        class={`${
                                          storage.status === 'available'
                                            ? 'text-green-600 dark:text-green-400'
                                            : 'text-red-600 dark:text-red-400'
                                        }`}
                                      >
                                        {storage.status || 'unknown'}
                                      </span>
                                    </td>
                                    <Show when={viewMode() === 'node'}>
                                      <td class="p-0.5 px-1.5 hidden lg:table-cell">
                                        <span class="text-xs text-gray-600 dark:text-gray-400">
                                          {storage.shared ? '✓' : '-'}
                                        </span>
                                      </td>
                                    </Show>

                                    <td class="p-0.5 px-1.5">
                                      <div class="relative min-w-[200px] h-3.5 rounded overflow-hidden bg-gray-200 dark:bg-gray-600">
                                        <div
                                          class={`absolute top-0 left-0 h-full ${getProgressBarColor(usagePercent)}`}
                                          style={{ width: `${usagePercent}%` }}
                                        />
                                        <span class="absolute inset-0 flex items-center justify-center text-[10px] font-medium text-gray-800 dark:text-gray-100 leading-none">
                                          <span class="whitespace-nowrap px-0.5">
                                            {formatPercent(usagePercent)} (
                                            {formatBytes(storage.used || 0, 0)}/
                                            {formatBytes(storage.total || 0, 0)})
                                          </span>
                                        </span>
                                      </div>
                                    </td>
                                    <td class="p-0.5 px-1.5 text-xs hidden sm:table-cell whitespace-nowrap">
                                      {formatBytes(storage.free || 0, 0)}
                                    </td>
                                    <td class="p-0.5 px-1.5 text-xs whitespace-nowrap">
                                      {formatBytes(storage.total || 0, 0)}
                                    </td>
                                    <td class="p-0.5 px-1.5"></td>
                                  </tr>
                                  <Show when={isCephStorage() && isExpanded()}>
                                    <tr
                                      class={`text-[11px] border-t border-gray-200 dark:border-gray-700 ${
                                        drawerDisabled()
                                          ? 'bg-gray-100/70 text-gray-400 dark:bg-gray-900/30 dark:text-gray-500'
                                          : 'bg-gray-50/60 text-gray-700 dark:bg-gray-900/30 dark:text-gray-300'
                                      }`}
                                    >
                                      <td colSpan={9} class="px-4 py-3">
                                        <div
                                          class={`grid gap-3 md:grid-cols-2 xl:grid-cols-2 ${
                                            drawerDisabled() ? 'opacity-60 pointer-events-none' : ''
                                          }`}
                                        >
                                          <div class="rounded-lg border border-gray-200 bg-white/80 p-4 shadow-sm dark:border-gray-600/60 dark:bg-gray-900/30">
                                            <div class="flex items-center gap-2 text-xs font-semibold text-gray-700 dark:text-gray-200">
                                              <span>Ceph Cluster</span>
                                              <span>{cephCluster()?.name || storage.instance}</span>
                                              <Show when={cephHealthLabel()}>
                                                <span class={`px-1.5 py-0.5 rounded text-[10px] font-medium ${cephHealthClass()}`}>
                                                  {cephHealthLabel()}
                                                </span>
                                              </Show>
                                            </div>
                                            <Show when={cephSummaryText()}>
                                              <div class="mt-2 text-[12px] text-gray-600 dark:text-gray-300">
                                                {cephSummaryText()}
                                              </div>
                                            </Show>
                                            <Show when={cephHealthMessage()}>
                                              <div class="mt-2 text-[11px] text-gray-500 dark:text-gray-400">
                                                {cephHealthMessage()}
                                              </div>
                                            </Show>
                                          </div>
                                          <div class="rounded-lg border border-gray-200 bg-white/80 p-4 shadow-sm dark:border-gray-600/60 dark:bg-gray-900/30 text-gray-600 dark:text-gray-300">
                                            <div class="text-xs font-semibold text-gray-700 dark:text-gray-200">
                                              Pools
                                            </div>
                                            <Show
                                              when={cephPoolsText()}
                                              fallback={
                                                <div class="mt-2 text-[12px] text-gray-500 dark:text-gray-400">
                                                  No pool data available
                                                </div>
                                              }
                                            >
                                              <div class="mt-2 text-[12px]">{cephPoolsText()}</div>
                                            </Show>
                                          </div>
                                        </div>
                                      </td>
                                    </tr>
                                  </Show>
                                  <Show
                                    when={
                                      storage.zfsPool &&
                                      (storage.zfsPool.state !== 'ONLINE' ||
                                        storage.zfsPool.readErrors > 0 ||
                                        storage.zfsPool.writeErrors > 0 ||
                                        storage.zfsPool.checksumErrors > 0)
                                    }
                                  >
                                    <tr class="bg-yellow-50 dark:bg-yellow-950/20 border-l-4 border-yellow-500">
                                      <td colspan="9" class="p-2">
                                        <div class="text-xs space-y-1">
                                          <div class="flex items-center gap-2">
                                            <span class="font-semibold text-yellow-700 dark:text-yellow-400">
                                              ZFS Pool Status:
                                            </span>
                                            <span
                                              class={`px-1.5 py-0.5 rounded text-xs font-medium ${
                                                storage.zfsPool!.state === 'ONLINE'
                                                  ? 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300'
                                                  : storage.zfsPool!.state === 'DEGRADED'
                                                    ? 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900 dark:text-yellow-300'
                                                    : 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300'
                                              }`}
                                            >
                                              {storage.zfsPool!.state}
                                            </span>
                                            <Show
                                              when={
                                                storage.zfsPool!.readErrors > 0 ||
                                                storage.zfsPool!.writeErrors > 0 ||
                                                storage.zfsPool!.checksumErrors > 0
                                              }
                                            >
                                              <span class="text-red-600 dark:text-red-400">
                                                Errors: {storage.zfsPool!.readErrors} read,{' '}
                                                {storage.zfsPool!.writeErrors} write,{' '}
                                                {storage.zfsPool!.checksumErrors} checksum
                                              </span>
                                            </Show>
                                          </div>
                                          <Show
                                            when={storage.zfsPool!.devices.some(
                                              (d) =>
                                                d.state !== 'ONLINE' ||
                                                d.readErrors > 0 ||
                                                d.writeErrors > 0 ||
                                                d.checksumErrors > 0,
                                            )}
                                          >
                                            <div class="ml-4 space-y-0.5">
                                              <For
                                                each={storage.zfsPool!.devices.filter(
                                                  (d) =>
                                                    d.state !== 'ONLINE' ||
                                                    d.readErrors > 0 ||
                                                    d.writeErrors > 0 ||
                                                    d.checksumErrors > 0,
                                                )}
                                              >
                                                {(device) => (
                                                  <div class="flex items-center gap-2 text-xs">
                                                    <span class="text-gray-600 dark:text-gray-400">
                                                      Device {device.name}:
                                                    </span>
                                                    <span
                                                      class={`${device.state !== 'ONLINE' ? 'text-red-600 dark:text-red-400' : 'text-yellow-600 dark:text-yellow-400'}`}
                                                    >
                                                      {device.state}
                                                      <Show
                                                        when={
                                                          device.readErrors > 0 ||
                                                          device.writeErrors > 0 ||
                                                          device.checksumErrors > 0
                                                        }
                                                      >
                                                        <span class="ml-1">
                                                          ({device.readErrors}R/{device.writeErrors}
                                                          W/{device.checksumErrors}C errors)
                                                        </span>
                                                      </Show>
                                                      <Show when={device.message?.trim()}>
                                                        {(message) => (
                                                          <span class="ml-1 text-gray-500 dark:text-gray-400">
                                                            {message()}
                                                          </span>
                                                        )}
                                                      </Show>
                                                    </span>
                                                  </div>
                                                )}
                                              </For>
                                            </div>
                                          </Show>
                                        </div>
                                      </td>
                                    </tr>
                                  </Show>
                                </>
                              );
                            }}
                          </For>
                        </>
                      );
                      }}
                    </For>
                  </tbody>
                </table>
              </div>
            </Card>
          </ComponentErrorBoundary>
        </Show>
      </Show>

      {/* Physical Disks Tab */}
      <Show when={tabView() === 'disks'}>
        <DiskList
          disks={state.physicalDisks || []}
          selectedNode={selectedNode()}
          searchTerm={searchTerm()}
        />
      </Show>
    </div>
  );
};

export default Storage;
