"use client";

import React, { useState, useRef, useEffect } from "react";
import { api } from "~/trpc/react";
import { ScriptCard } from "./ScriptCard";
import { ScriptCardList } from "./ScriptCardList";
import { ScriptDetailModal } from "./ScriptDetailModal";
import { CategorySidebar } from "./CategorySidebar";
import { FilterBar, type FilterState } from "./FilterBar";
import { ViewToggle } from "./ViewToggle";
import { Button } from "./ui/button";
import type { ScriptCard as ScriptCardType } from "~/types/script";
import type { Server } from "~/types/server";
import { getDefaultFilters, mergeFiltersWithDefaults } from "./filterUtils";

interface DownloadedScriptsTabProps {
  onInstallScript?: (
    scriptPath: string,
    scriptName: string,
    mode?: "local" | "ssh",
    server?: Server,
  ) => void;
}

export function DownloadedScriptsTab({
  onInstallScript,
}: DownloadedScriptsTabProps) {
  const [selectedSlug, setSelectedSlug] = useState<string | null>(null);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
  const [viewMode, setViewMode] = useState<"card" | "list">("card");
  const [filters, setFilters] = useState<FilterState>(getDefaultFilters());
  const [saveFiltersEnabled, setSaveFiltersEnabled] = useState(false);
  const [isLoadingFilters, setIsLoadingFilters] = useState(true);
  const gridRef = useRef<HTMLDivElement>(null);

  const {
    data: scriptCardsData,
    isLoading: githubLoading,
    error: githubError,
    refetch,
  } = api.scripts.getScriptCardsWithCategories.useQuery();
  const {
    data: localScriptsData,
    isLoading: localLoading,
    error: localError,
  } = api.scripts.getAllDownloadedScripts.useQuery();
  const { data: scriptData } = api.scripts.getScriptBySlug.useQuery(
    { slug: selectedSlug ?? "" },
    { enabled: !!selectedSlug },
  );

  // Load SAVE_FILTER setting, saved filters, and view mode on component mount
  useEffect(() => {
    const loadSettings = async () => {
      try {
        // Load SAVE_FILTER setting
        const saveFilterResponse = await fetch("/api/settings/save-filter");
        let saveFilterEnabled = false;
        if (saveFilterResponse.ok) {
          const saveFilterData = await saveFilterResponse.json();
          saveFilterEnabled = saveFilterData.enabled ?? false;
          setSaveFiltersEnabled(saveFilterEnabled);
        }

        // Load saved filters if SAVE_FILTER is enabled
        if (saveFilterEnabled) {
          const filtersResponse = await fetch("/api/settings/filters");
          if (filtersResponse.ok) {
            const filtersData = (await filtersResponse.json()) as {
              filters?: Partial<FilterState>;
            };
            if (filtersData.filters) {
              setFilters(mergeFiltersWithDefaults(filtersData.filters));
            }
          }
        }

        // Load view mode
        const viewModeResponse = await fetch("/api/settings/view-mode");
        if (viewModeResponse.ok) {
          const viewModeData = await viewModeResponse.json();
          const viewMode = viewModeData.viewMode;
          if (
            viewMode &&
            typeof viewMode === "string" &&
            (viewMode === "card" || viewMode === "list")
          ) {
            setViewMode(viewMode);
          }
        }
      } catch (error) {
        console.error("Error loading settings:", error);
      } finally {
        setIsLoadingFilters(false);
      }
    };

    void loadSettings();
  }, []);

  // Save filters when they change (if SAVE_FILTER is enabled)
  useEffect(() => {
    if (!saveFiltersEnabled || isLoadingFilters) return;

    const saveFilters = async () => {
      try {
        await fetch("/api/settings/filters", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ filters }),
        });
      } catch (error) {
        console.error("Error saving filters:", error);
      }
    };

    // Debounce the save operation
    const timeoutId = setTimeout(() => void saveFilters(), 500);
    return () => clearTimeout(timeoutId);
  }, [filters, saveFiltersEnabled, isLoadingFilters]);

  // Save view mode when it changes
  useEffect(() => {
    if (isLoadingFilters) return;

    const saveViewMode = async () => {
      try {
        await fetch("/api/settings/view-mode", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ viewMode }),
        });
      } catch (error) {
        console.error("Error saving view mode:", error);
      }
    };

    // Debounce the save operation
    const timeoutId = setTimeout(() => void saveViewMode(), 300);
    return () => clearTimeout(timeoutId);
  }, [viewMode, isLoadingFilters]);

  // Extract categories from metadata
  const categories = React.useMemo((): string[] => {
    if (!scriptCardsData?.success || !scriptCardsData.metadata?.categories)
      return [];

    return (scriptCardsData.metadata.categories as any[])
      .filter((cat) => cat.id !== 0) // Exclude Miscellaneous for main list
      .sort((a, b) => a.sort_order - b.sort_order)
      .map((cat) => cat.name as string)
      .filter((name): name is string => typeof name === "string");
  }, [scriptCardsData]);

  // Get GitHub scripts with download status (deduplicated)
  const combinedScripts = React.useMemo((): ScriptCardType[] => {
    if (!scriptCardsData?.success) return [];

    // Use Map to deduplicate by slug/name
    const scriptMap = new Map<string, ScriptCardType>();

    scriptCardsData.cards?.forEach((script: ScriptCardType) => {
      if (script?.name && script?.slug) {
        // Use slug as unique identifier, only keep first occurrence
        if (!scriptMap.has(script.slug)) {
          scriptMap.set(script.slug, {
            ...script,
            source: "github" as const,
            isDownloaded: false, // Will be updated by status check
            isUpToDate: false, // Will be updated by status check
          });
        }
      }
    });

    return Array.from(scriptMap.values());
  }, [scriptCardsData]);

  // Update scripts with download status and filter to only downloaded scripts
  const downloadedScripts = React.useMemo((): ScriptCardType[] => {
    // Helper to normalize identifiers so underscores vs hyphens don't break matches
    const normalizeId = (s?: string): string =>
      (s ?? "")
        .toLowerCase()
        .replace(/\.(sh|bash|py|js|ts)$/g, "")
        .replace(/[^a-z0-9]+/g, "-")
        .replace(/^-+|-+$/g, "");

    return combinedScripts
      .map((script) => {
        if (!script?.name) {
          return script; // Return as-is if invalid
        }

        // Check if there's a corresponding local script
        const hasLocalVersion =
          localScriptsData?.scripts?.some((local) => {
            if (!local?.name) return false;

            // Primary: Exact slug-to-slug matching (most reliable, prevents false positives)
            if (local.slug && script.slug) {
              if (local.slug.toLowerCase() === script.slug.toLowerCase()) {
                return true;
              }
            }

            // Secondary: Check install basenames (for edge cases where install script names differ from slugs)
            // Only use normalized matching for install basenames, not for slug/name matching
            const normalizedLocal = normalizeId(local.name);
            const matchesInstallBasename =
              (script as any)?.install_basenames?.some(
                (base: string) => normalizeId(base) === normalizedLocal,
              ) ?? false;
            return matchesInstallBasename;
          }) ?? false;

        return {
          ...script,
          isDownloaded: hasLocalVersion,
        };
      })
      .filter((script) => script.isDownloaded); // Only show downloaded scripts
  }, [combinedScripts, localScriptsData]);

  // Count scripts per category (using downloaded scripts only)
  const categoryCounts = React.useMemo((): Record<string, number> => {
    if (!scriptCardsData?.success) return {};

    const counts: Record<string, number> = {};

    // Initialize all categories with 0
    categories.forEach((categoryName: string) => {
      counts[categoryName] = 0;
    });

    // Count each unique downloaded script only once per category
    downloadedScripts.forEach((script) => {
      if (script.categoryNames && script.slug) {
        const countedCategories = new Set<string>();
        script.categoryNames.forEach((categoryName: unknown) => {
          if (
            typeof categoryName === "string" &&
            counts[categoryName] !== undefined &&
            !countedCategories.has(categoryName)
          ) {
            countedCategories.add(categoryName);
            counts[categoryName]++;
          }
        });
      }
    });

    return counts;
  }, [categories, downloadedScripts, scriptCardsData?.success]);

  // Filter scripts based on all filters and category
  const filteredScripts = React.useMemo((): ScriptCardType[] => {
    let scripts = downloadedScripts;

    // Filter by search query
    if (filters.searchQuery?.trim()) {
      const query = filters.searchQuery.toLowerCase().trim();

      if (query.length >= 1) {
        scripts = scripts.filter((script) => {
          if (!script || typeof script !== "object") {
            return false;
          }

          const name = (script.name ?? "").toLowerCase();
          const slug = (script.slug ?? "").toLowerCase();

          return name.includes(query) ?? slug.includes(query);
        });
      }
    }

    // Filter by category using real category data from downloaded scripts
    if (selectedCategory) {
      scripts = scripts.filter((script) => {
        if (!script) return false;

        // Check if the downloaded script has categoryNames that include the selected category
        return script.categoryNames?.includes(selectedCategory) ?? false;
      });
    }

    // Filter by updateable status
    if (filters.showUpdatable !== null) {
      scripts = scripts.filter((script) => {
        if (!script) return false;
        const isUpdatable = script.updateable ?? false;
        return filters.showUpdatable ? isUpdatable : !isUpdatable;
      });
    }

    // Filter by script types
    if (filters.selectedTypes.length > 0) {
      scripts = scripts.filter((script) => {
        if (!script) return false;
        const scriptType = (script.type ?? "").toLowerCase();

        // Map non-standard types to standard categories
        const mappedType = scriptType === "turnkey" ? "ct" : scriptType;

        return filters.selectedTypes.some(
          (type) => type.toLowerCase() === mappedType,
        );
      });
    }

    // Filter by repositories
    if (filters.selectedRepositories.length > 0) {
      scripts = scripts.filter((script) => {
        if (!script) return false;
        const repoUrl = script.repository_url;

        // If script has no repository_url, exclude it when filtering by repositories
        if (!repoUrl) {
          return false;
        }

        // Only include scripts from selected repositories
        return filters.selectedRepositories.includes(repoUrl);
      });
    }

    // Apply sorting
    scripts.sort((a, b) => {
      if (!a || !b) return 0;

      let compareValue = 0;

      switch (filters.sortBy) {
        case "name":
          compareValue = (a.name ?? "").localeCompare(b.name ?? "");
          break;
        case "created":
          // Get creation date from script metadata in JSON format (date_created: "YYYY-MM-DD")
          const aCreated = a?.date_created ?? "";
          const bCreated = b?.date_created ?? "";

          // If both have dates, compare them directly
          if (aCreated && bCreated) {
            // For dates: asc = oldest first (2020 before 2024), desc = newest first (2024 before 2020)
            compareValue = aCreated.localeCompare(bCreated);
          } else if (aCreated && !bCreated) {
            // Scripts with dates come before scripts without dates
            compareValue = -1;
          } else if (!aCreated && bCreated) {
            // Scripts without dates come after scripts with dates
            compareValue = 1;
          } else {
            // Both have no dates, fallback to name comparison
            compareValue = (a.name ?? "").localeCompare(b.name ?? "");
          }
          break;
        default:
          compareValue = (a.name ?? "").localeCompare(b.name ?? "");
      }

      // Apply sort order
      return filters.sortOrder === "asc" ? compareValue : -compareValue;
    });

    return scripts;
  }, [downloadedScripts, filters, selectedCategory]);

  // Calculate filter counts for FilterBar
  const filterCounts = React.useMemo(() => {
    const updatableCount = downloadedScripts.filter(
      (script) => script?.updateable,
    ).length;

    return { installedCount: downloadedScripts.length, updatableCount };
  }, [downloadedScripts]);

  // Handle filter changes
  const handleFiltersChange = (newFilters: FilterState) => {
    setFilters(newFilters);
  };

  // Handle category selection with auto-scroll
  const handleCategorySelect = (category: string | null) => {
    setSelectedCategory(category);
  };

  // Auto-scroll effect when category changes
  useEffect(() => {
    if (selectedCategory && gridRef.current) {
      const timeoutId = setTimeout(() => {
        gridRef.current?.scrollIntoView({
          behavior: "smooth",
          block: "start",
          inline: "nearest",
        });
      }, 100);

      return () => clearTimeout(timeoutId);
    }
  }, [selectedCategory]);

  const handleCardClick = (scriptCard: ScriptCardType) => {
    // All scripts are GitHub scripts, open modal
    setSelectedSlug(scriptCard.slug);
    setIsModalOpen(true);
  };

  const handleCloseModal = () => {
    setIsModalOpen(false);
    setSelectedSlug(null);
  };

  if (githubLoading || localLoading) {
    return (
      <div className="flex items-center justify-center py-12">
        <div className="border-primary h-8 w-8 animate-spin rounded-full border-b-2"></div>
        <span className="text-muted-foreground ml-2">
          Loading downloaded scripts...
        </span>
      </div>
    );
  }

  if (githubError || localError) {
    return (
      <div className="py-12 text-center">
        <div className="text-error mb-4">
          <svg
            className="mx-auto mb-2 h-12 w-12"
            fill="none"
            stroke="currentColor"
            viewBox="0 0 24 24"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth={2}
              d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
            />
          </svg>
          <p className="text-lg font-medium">
            Failed to load downloaded scripts
          </p>
          <p className="text-muted-foreground mt-1 text-sm">
            {githubError?.message ??
              localError?.message ??
              "Unknown error occurred"}
          </p>
        </div>
        <Button
          onClick={() => refetch()}
          variant="default"
          size="default"
          className="mt-4"
        >
          Try Again
        </Button>
      </div>
    );
  }

  if (!downloadedScripts?.length) {
    return (
      <div className="py-12 text-center">
        <div className="text-muted-foreground">
          <svg
            className="mx-auto mb-4 h-12 w-12"
            fill="none"
            stroke="currentColor"
            viewBox="0 0 24 24"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth={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>
          <p className="text-lg font-medium">No downloaded scripts found</p>
          <p className="text-muted-foreground mt-1 text-sm">
            You haven&apos;t downloaded any scripts yet. Visit the Available
            Scripts tab to download some scripts.
          </p>
        </div>
      </div>
    );
  }

  return (
    <div className="space-y-6">
      <div className="flex flex-col gap-4 lg:flex-row lg:gap-6">
        {/* Category Sidebar */}
        <div className="order-2 flex-shrink-0 lg:order-1">
          <CategorySidebar
            categories={categories}
            categoryCounts={categoryCounts}
            totalScripts={downloadedScripts.length}
            selectedCategory={selectedCategory}
            onCategorySelect={handleCategorySelect}
          />
        </div>

        {/* Main Content */}
        <div className="order-1 min-w-0 flex-1 lg:order-2" ref={gridRef}>
          {/* Enhanced Filter Bar */}
          <FilterBar
            filters={filters}
            onFiltersChange={handleFiltersChange}
            totalScripts={downloadedScripts.length}
            filteredCount={filteredScripts.length}
            updatableCount={filterCounts.updatableCount}
            saveFiltersEnabled={saveFiltersEnabled}
            isLoadingFilters={isLoadingFilters}
          />

          {/* View Toggle */}
          <ViewToggle viewMode={viewMode} onViewModeChange={setViewMode} />

          {/* Scripts Grid */}
          {filteredScripts.length === 0 &&
          (filters.searchQuery ||
            selectedCategory ||
            filters.showUpdatable !== null ||
            filters.selectedTypes.length > 0) ? (
            <div className="py-12 text-center">
              <div className="text-muted-foreground">
                <svg
                  className="mx-auto mb-4 h-12 w-12"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                >
                  <path
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    strokeWidth={2}
                    d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
                  />
                </svg>
                <p className="text-lg font-medium">
                  No matching downloaded scripts found
                </p>
                <p className="text-muted-foreground mt-1 text-sm">
                  Try different filter settings or clear all filters.
                </p>
                <div className="mt-4 flex justify-center gap-2">
                  {filters.searchQuery && (
                    <Button
                      onClick={() =>
                        handleFiltersChange({ ...filters, searchQuery: "" })
                      }
                      variant="default"
                      size="default"
                    >
                      Clear Search
                    </Button>
                  )}
                  {selectedCategory && (
                    <Button
                      onClick={() => handleCategorySelect(null)}
                      variant="secondary"
                      size="default"
                    >
                      Clear Category
                    </Button>
                  )}
                </div>
              </div>
            </div>
          ) : viewMode === "card" ? (
            <div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
              {filteredScripts.map((script, index) => {
                // Add validation to ensure script has required properties
                if (!script || typeof script !== "object") {
                  return null;
                }

                // Create a unique key by combining slug, name, and index to handle duplicates
                const uniqueKey = `${script.slug ?? "unknown"}-${script.name ?? "unnamed"}-${index}`;

                return (
                  <ScriptCard
                    key={uniqueKey}
                    script={script}
                    onClick={handleCardClick}
                  />
                );
              })}
            </div>
          ) : (
            <div className="space-y-3">
              {filteredScripts.map((script, index) => {
                // Add validation to ensure script has required properties
                if (!script || typeof script !== "object") {
                  return null;
                }

                // Create a unique key by combining slug, name, and index to handle duplicates
                const uniqueKey = `${script.slug ?? "unknown"}-${script.name ?? "unnamed"}-${index}`;

                return (
                  <ScriptCardList
                    key={uniqueKey}
                    script={script}
                    onClick={handleCardClick}
                  />
                );
              })}
            </div>
          )}

          <ScriptDetailModal
            script={scriptData?.success ? scriptData.script : null}
            isOpen={isModalOpen}
            onClose={handleCloseModal}
            onInstallScript={onInstallScript}
          />
        </div>
      </div>
    </div>
  );
}
