import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  IconAlertCircle,
  IconDownload,
  IconPalette,
  IconRefresh,
  IconRobot,
  IconSearch,
  IconTrash,
  IconUser,
  IconX,
} from '@tabler/icons-react';
import {
  Accordion,
  ActionIcon,
  Alert,
  Badge,
  Button,
  Card,
  ColorSwatch,
  Divider,
  Group,
  Loader,
  Modal,
  NavLink,
  Pagination,
  PasswordInput,
  rem,
  ScrollArea,
  Stack,
  Text,
  TextInput,
  Title,
  Tooltip,
  useMantineTheme,
} from '@mantine/core';
import { useDebouncedValue } from '@mantine/hooks';
import { deleteModel, getInstalledModels, pullModel, type OllamaModel } from '@/app/actions/ollama';

// Type for the scraped models JSON
interface ModelInfo {
  tags: string[];
  capabilities: string[];
}

interface OllamaModelsData {
  generatedAt: string;
  modelCount: number;
  models: Record<string, ModelInfo>;
}

interface User {
  id: string;
  username: string;
  accentColor?: string;
}

interface SettingsModalProps {
  opened: boolean;
  close: () => void;
  primaryColor: string;
  setPrimaryColor: (color: string) => void;
}

// Session-level cache for available models (survives modal close/open)
let availableModelsCache: OllamaModelsData | null = null;
let installedModelsCache: OllamaModel[] | null = null;

export function SettingsModal({
  opened,
  close,
  primaryColor,
  setPrimaryColor,
}: SettingsModalProps) {
  const theme = useMantineTheme();
  const [activeTab, setActiveTab] = useState<'appearance' | 'account' | 'models'>('appearance');

  // Account State
  const [user, setUser] = useState<User | null>(null);
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [isLoginMode, setIsLoginMode] = useState(true);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);

  // Models State
  const [installedModels, setInstalledModels] = useState<OllamaModel[]>(installedModelsCache || []);
  const [availableModels, setAvailableModels] = useState<OllamaModelsData | null>(
    availableModelsCache
  );
  const [loadingInstalled, setLoadingInstalled] = useState(false);
  const [loadingAvailable, setLoadingAvailable] = useState(false);
  const [pullingModel, setPullingModel] = useState<string | null>(null);
  const [pullError, setPullError] = useState('');

  // Search state for available models (with debounce for performance)
  const [modelSearch, setModelSearch] = useState('');
  const [debouncedSearch] = useDebouncedValue(modelSearch, 200);

  // Pagination state
  const MODELS_PER_PAGE = 20;
  const [currentPage, setCurrentPage] = useState(1);

  // Track if we've fetched this session
  const hasFetchedInstalled = useRef(false);
  const hasFetchedAvailable = useRef(false);

  // Get list of model names sorted alphabetically
  const modelNames = useMemo(
    () => (availableModels ? Object.keys(availableModels.models).sort() : []),
    [availableModels]
  );

  // Filter models based on debounced search
  const filteredModels = useMemo(
    () =>
      modelNames.filter((name) =>
        name.toLowerCase().includes(debouncedSearch.toLowerCase().trim())
      ),
    [modelNames, debouncedSearch]
  );

  // Paginated models - only render what's visible
  const totalPages = Math.ceil(filteredModels.length / MODELS_PER_PAGE);
  const paginatedModels = useMemo(() => {
    const start = (currentPage - 1) * MODELS_PER_PAGE;
    return filteredModels.slice(start, start + MODELS_PER_PAGE);
  }, [filteredModels, currentPage]);

  // Reset to page 1 when search changes
  useEffect(() => {
    setCurrentPage(1);
  }, [debouncedSearch]);

  // Fetch available models from the static JSON
  const fetchAvailableModels = useCallback(async (force = false) => {
    if (!force && availableModelsCache) {
      setAvailableModels(availableModelsCache);
      return;
    }

    setLoadingAvailable(true);
    try {
      const response = await fetch('/ollama-models.json');
      if (response.ok) {
        const data: OllamaModelsData = await response.json();
        availableModelsCache = data;
        setAvailableModels(data);
      }
    } catch (e) {
      console.error('Failed to fetch available models:', e);
    } finally {
      setLoadingAvailable(false);
    }
  }, []);

  // Fetch installed models from Ollama
  const fetchInstalledModels = useCallback(async (force = false) => {
    if (!force && installedModelsCache) {
      setInstalledModels(installedModelsCache);
      return;
    }

    setLoadingInstalled(true);
    try {
      const list = await getInstalledModels();
      installedModelsCache = list;
      setInstalledModels(list);
    } catch (e) {
      console.error('Failed to fetch installed models:', e);
    } finally {
      setLoadingInstalled(false);
    }
  }, []);

  // Check login status on mount
  useEffect(() => {
    if (opened) {
      fetchUser();
      if (activeTab === 'models') {
        if (!hasFetchedInstalled.current) {
          fetchInstalledModels();
          hasFetchedInstalled.current = true;
        }
        if (!hasFetchedAvailable.current) {
          fetchAvailableModels();
          hasFetchedAvailable.current = true;
        }
      }
    }
  }, [opened, activeTab, fetchInstalledModels, fetchAvailableModels]);

  const fetchUser = async () => {
    try {
      const res = await fetch('/api/auth/me');
      const data = await res.json();
      if (data.user) {
        setUser(data.user);
      } else {
        setUser(null);
      }
    } catch (e) {
      console.error(e);
    }
  };

  const handlePullModel = async (modelName: string, tag: string) => {
    const fullModelName = `${modelName}:${tag}`;

    setPullingModel(fullModelName);
    setPullError('');

    try {
      const result = await pullModel(fullModelName);
      if (result.success) {
        // Force refresh installed models
        await fetchInstalledModels(true);
      } else {
        setPullError(result.message);
      }
    } catch (e) {
      console.error(e);
      setPullError('An error occurred while pulling the model');
    } finally {
      setPullingModel(null);
    }
  };

  const handleDeleteModel = async (name: string) => {
    // eslint-disable-next-line no-alert
    if (!confirm(`Are you sure you want to delete ${name}?`)) {
      return;
    }
    try {
      await deleteModel(name);
      // Force refresh installed models
      await fetchInstalledModels(true);
    } catch (e) {
      console.error(e);
    }
  };

  const handleAuth = async () => {
    setError('');
    setLoading(true);
    const endpoint = isLoginMode ? '/api/auth/login' : '/api/auth/register';

    try {
      const res = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password }),
      });

      const data = await res.json();

      if (!res.ok) {
        throw new Error(data.error || 'Something went wrong');
      }

      // Refresh user state
      await fetchUser();
      setUsername('');
      setPassword('');
    } catch (err: unknown) {
      setError(err instanceof Error ? err.message : 'An error occurred');
    } finally {
      setLoading(false);
    }
  };

  const handleLogout = async () => {
    await fetch('/api/auth/logout', { method: 'POST' });
    setUser(null);
  };

  const handleAccentColorChange = async (color: string) => {
    // Update local state immediately for responsiveness
    setPrimaryColor(color);

    // If user is logged in, persist to database
    if (user) {
      try {
        await fetch('/api/user/settings', {
          method: 'PATCH',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ accentColor: color }),
        });
      } catch (e) {
        console.error('Failed to save accent color:', e);
      }
    }
  };

  const colors = Object.keys(theme.colors).filter(
    (color) => color !== 'dark' && color !== 'gray' && color !== 'white' && color !== 'black'
  );

  // Check if a model (base name) is already installed
  // Returns list of installed tags for the given model name
  const getInstalledTags = (modelName: string): string[] => {
    return installedModels
      .filter((m) => {
        // Handle both "modelName:tag" and "modelName" (defaults to "latest")
        const [baseName] = m.name.split(':');
        return baseName === modelName;
      })
      .map((m) => {
        const parts = m.name.split(':');
        // If no tag specified, it's "latest"
        return parts.length > 1 ? parts[1] : 'latest';
      });
  };

  return (
    <Modal
      opened={opened}
      onClose={close}
      withCloseButton={false}
      size="xl"
      padding={0}
      radius="xl"
    >
      <Group align="stretch" gap={0} style={{ minHeight: 600, overflow: 'hidden' }}>
        {/* Left Sidebar */}
        <Stack
          gap="xs"
          w={220}
          p="sm"
          bg="var(--mantine-color-default-hover)"
          style={{
            borderRight: '1px solid var(--mantine-color-default-border)',
          }}
        >
          <NavLink
            active={activeTab === 'appearance'}
            label="Appearance"
            leftSection={<IconPalette size={18} stroke={1.5} />}
            variant="light"
            color={primaryColor}
            onClick={() => setActiveTab('appearance')}
            style={{ borderRadius: 'var(--mantine-radius-lg)' }}
          />
          <NavLink
            active={activeTab === 'models'}
            label="Models"
            leftSection={<IconRobot size={18} stroke={1.5} />}
            variant="light"
            color={primaryColor}
            onClick={() => setActiveTab('models')}
            style={{ borderRadius: 'var(--mantine-radius-lg)' }}
          />
          <NavLink
            active={activeTab === 'account'}
            label="Account"
            leftSection={<IconUser size={18} stroke={1.5} />}
            variant="light"
            color={primaryColor}
            onClick={() => setActiveTab('account')}
            style={{ borderRadius: 'var(--mantine-radius-lg)' }}
          />
        </Stack>

        {/* Right Content */}
        <Stack p="xl" style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
          <ActionIcon
            onClick={close}
            variant="subtle"
            color="gray"
            size="sm"
            style={{ position: 'absolute', top: rem(15), right: rem(15), zIndex: 1 }}
          >
            <IconX size={20} />
          </ActionIcon>

          {activeTab === 'appearance' && (
            <>
              <Title order={4}>Appearance</Title>
              <Text size="sm" c="dimmed">
                Customize the look and feel of the application.
              </Text>

              <Divider my="sm" />

              <Stack gap="xs">
                <Text size="sm" fw={500}>
                  Accent Color
                </Text>
                <Group gap="xs">
                  {colors.map((color) => (
                    <ColorSwatch
                      key={color}
                      component="button"
                      color={theme.colors[color][6]}
                      onClick={() => handleAccentColorChange(color)}
                      style={{ color: '#fff', cursor: 'pointer' }}
                      withShadow
                    >
                      {primaryColor === color && <IconPalette size={12} />}
                    </ColorSwatch>
                  ))}
                </Group>
              </Stack>
            </>
          )}

          {activeTab === 'models' && (
            <Stack gap="md" style={{ flex: 1, overflow: 'hidden' }}>
              <div>
                <Title order={4}>Models</Title>
                <Text size="sm" c="dimmed">
                  Download and manage AI models from the Ollama registry.
                </Text>
              </div>

              {pullError && (
                <Alert
                  icon={<IconAlertCircle size={16} />}
                  title="Error"
                  color="red"
                  withCloseButton
                  onClose={() => setPullError('')}
                >
                  {pullError}
                </Alert>
              )}

              {pullingModel && (
                <Alert icon={<Loader size={16} />} title="Downloading..." color="blue">
                  Pulling {pullingModel}. This may take a while.
                </Alert>
              )}

              <ScrollArea style={{ flex: 1 }} offsetScrollbars>
                <Stack gap="lg">
                  {/* Installed Models Section */}
                  <div>
                    <Group justify="space-between" mb="xs">
                      <Text size="sm" fw={600}>
                        Installed Models
                      </Text>
                      <Tooltip label="Refresh">
                        <ActionIcon
                          variant="subtle"
                          color="gray"
                          size="sm"
                          onClick={() => fetchInstalledModels(true)}
                          loading={loadingInstalled}
                        >
                          <IconRefresh size={16} />
                        </ActionIcon>
                      </Tooltip>
                    </Group>

                    {loadingInstalled && installedModels.length === 0 ? (
                      <Group justify="center" py="md">
                        <Loader size="sm" />
                      </Group>
                    ) : installedModels.length === 0 ? (
                      <Card withBorder padding="md" radius="md">
                        <Text c="dimmed" size="sm" ta="center">
                          No models installed yet. Browse available models below.
                        </Text>
                      </Card>
                    ) : (
                      <Stack gap="xs">
                        {installedModels.map((model) => (
                          <Card key={model.digest} withBorder padding="sm" radius="md">
                            <Group justify="space-between">
                              <div>
                                <Text fw={500} size="sm">
                                  {model.name}
                                </Text>
                                <Group gap="xs" mt={4}>
                                  <Badge size="xs" variant="light" color="gray">
                                    {(model.size / 1024 / 1024 / 1024).toFixed(2)} GB
                                  </Badge>
                                  <Badge size="xs" variant="light" color="blue">
                                    {model.details.parameter_size}
                                  </Badge>
                                  <Badge size="xs" variant="light" color="orange">
                                    {model.details.quantization_level}
                                  </Badge>
                                </Group>
                              </div>
                              <ActionIcon
                                color="red"
                                variant="subtle"
                                onClick={() => handleDeleteModel(model.name)}
                              >
                                <IconTrash size={16} />
                              </ActionIcon>
                            </Group>
                          </Card>
                        ))}
                      </Stack>
                    )}
                  </div>

                  <Divider />

                  {/* Available Models Section */}
                  <div>
                    <Group justify="space-between" mb="xs">
                      <Text size="sm" fw={600}>
                        Available Models
                      </Text>
                      {availableModels && (
                        <Text size="xs" c="dimmed">
                          {filteredModels.length === modelNames.length
                            ? `${modelNames.length} models`
                            : `${filteredModels.length} of ${modelNames.length} models`}
                        </Text>
                      )}
                    </Group>

                    <TextInput
                      placeholder="Search models..."
                      leftSection={<IconSearch size={16} />}
                      value={modelSearch}
                      onChange={(e) => setModelSearch(e.currentTarget.value)}
                      mb="sm"
                    />

                    {loadingAvailable ? (
                      <Group justify="center" py="md">
                        <Loader size="sm" />
                      </Group>
                    ) : (
                      <>
                        <Accordion
                          variant="separated"
                          radius="md"
                          styles={{
                            item: {
                              border: '1px solid var(--mantine-color-default-border)',
                              backgroundColor: 'var(--mantine-color-default)',
                            },
                            control: {
                              padding: 'var(--mantine-spacing-sm)',
                            },
                            label: {
                              padding: 0,
                            },
                          }}
                        >
                          {paginatedModels.map((modelName) => {
                            const modelInfo = availableModels?.models[modelName];
                            const tags = modelInfo?.tags || [];
                            const capabilities = modelInfo?.capabilities || [];
                            const installedTags = getInstalledTags(modelName);

                            return (
                              <Accordion.Item key={modelName} value={modelName}>
                                <Accordion.Control>
                                  <Group justify="space-between" pr="sm" wrap="nowrap">
                                    <Group gap="xs" wrap="nowrap">
                                      <Text fw={500} size="sm">
                                        {modelName}
                                      </Text>
                                      {capabilities.map((cap) => (
                                        <Badge
                                          key={cap}
                                          size="xs"
                                          variant="light"
                                          color={
                                            cap === 'vision'
                                              ? 'violet'
                                              : cap === 'tools'
                                                ? 'blue'
                                                : cap === 'thinking'
                                                  ? 'orange'
                                                  : cap === 'embedding'
                                                    ? 'teal'
                                                    : 'gray'
                                          }
                                        >
                                          {cap}
                                        </Badge>
                                      ))}
                                    </Group>
                                    <Group gap="xs" wrap="nowrap">
                                      <Badge size="xs" variant="light" color="gray">
                                        {tags.length} tags
                                      </Badge>
                                      {installedTags.length > 0 && (
                                        <Badge size="xs" variant="light" color="green">
                                          {installedTags.length} installed
                                        </Badge>
                                      )}
                                    </Group>
                                  </Group>
                                </Accordion.Control>
                                <Accordion.Panel>
                                  <Stack gap="xs">
                                    {tags.map((tag) => {
                                      const isInstalled = installedTags.includes(tag);
                                      const isPulling = pullingModel === `${modelName}:${tag}`;

                                      return (
                                        <Group key={tag} justify="space-between">
                                          <Text size="sm" c={isInstalled ? 'dimmed' : undefined}>
                                            {modelName}:{tag}
                                          </Text>
                                          {isInstalled ? (
                                            <Badge size="xs" variant="light" color="green">
                                              Installed
                                            </Badge>
                                          ) : (
                                            <Button
                                              size="xs"
                                              variant="light"
                                              color={primaryColor}
                                              leftSection={<IconDownload size={14} />}
                                              onClick={() => handlePullModel(modelName, tag)}
                                              loading={isPulling}
                                              disabled={!!pullingModel}
                                            >
                                              Download
                                            </Button>
                                          )}
                                        </Group>
                                      );
                                    })}
                                  </Stack>
                                </Accordion.Panel>
                              </Accordion.Item>
                            );
                          })}
                        </Accordion>

                        {filteredModels.length === 0 && debouncedSearch && (
                          <Text c="dimmed" size="sm" ta="center" py="md">
                            No models found matching &quot;{debouncedSearch}&quot;
                          </Text>
                        )}

                        {totalPages > 1 && (
                          <Group justify="center" mt="md">
                            <Pagination
                              total={totalPages}
                              value={currentPage}
                              onChange={setCurrentPage}
                              size="sm"
                            />
                          </Group>
                        )}
                      </>
                    )}

                    {availableModels && (
                      <Text size="xs" c="dimmed" mt="md">
                        Model list updated:{' '}
                        {new Date(availableModels.generatedAt).toLocaleDateString()}
                      </Text>
                    )}
                  </div>
                </Stack>
              </ScrollArea>
            </Stack>
          )}

          {activeTab === 'account' && (
            <>
              <Title order={4}>Account</Title>
              <Text size="sm" c="dimmed">
                Manage your account and chat history.
              </Text>
              <Divider my="sm" />

              {user ? (
                <Stack>
                  <Text>
                    Logged in as <b>{user.username}</b>
                  </Text>
                  <Button color="red" variant="light" onClick={handleLogout}>
                    Log out
                  </Button>
                </Stack>
              ) : (
                <Stack>
                  {error && (
                    <Alert icon={<IconAlertCircle size={16} />} title="Error" color="red">
                      {error}
                    </Alert>
                  )}
                  <TextInput
                    label="Username"
                    placeholder="Enter username"
                    value={username}
                    onChange={(e) => setUsername(e.target.value)}
                  />
                  <PasswordInput
                    label="Password"
                    placeholder="Enter password"
                    value={password}
                    onChange={(e) => setPassword(e.target.value)}
                  />
                  <Group justify="space-between" mt="md">
                    <Text
                      size="xs"
                      style={{ cursor: 'pointer' }}
                      c="blue"
                      onClick={() => setIsLoginMode(!isLoginMode)}
                    >
                      {isLoginMode ? 'Need an account? Register' : 'Have an account? Login'}
                    </Text>
                    <Button onClick={handleAuth} loading={loading} color={primaryColor}>
                      {isLoginMode ? 'Login' : 'Register'}
                    </Button>
                  </Group>
                </Stack>
              )}
            </>
          )}
        </Stack>
      </Group>
    </Modal>
  );
}
