import { useState, useMemo, useCallback, type DragEvent } from 'react';
import { toast } from 'sonner';
import { Bot, Terminal, Eye, Sparkles, X, Plus, RefreshCw } from 'lucide-react';
import { useQueryClient } from '@tanstack/react-query';
import { useClient } from 'hooks/useClient';
import type { LayoutNode, PanelComponents } from 'officerdev';
import { WorkspaceLayout } from 'officerdev';
import { useAuth } from 'hooks/useAuth';

import { createSettingsPanelComponents, type SettingsSectionGroup } from './SettingsPanel';
import { useSettings } from 'state/useSettings';
import type { UserSettings } from 'state/useSettings';
import { usePiModels, useVisiblePiModels, modelKey, getProviderDisplayName, type ModelOption } from 'state/useModels';
import { useAccessPolicy } from 'state/useAccessPolicy';
import { useUserState } from 'state/useUserState';
import { AIHarnessesSection } from './ServerSettings/AIHarnessesSection';
import { AIModels } from './ProfileSettings/AIModels';

const PROVIDER_DISPLAY: Record<string, string> = {
  anthropic: 'Anthropic',
  openai: 'OpenAI',
  zai: 'ZAI',
  google: 'Google',
  groq: 'Groq',
  mistral: 'Mistral',
  xai: 'xAI',
  openrouter: 'OpenRouter',
  huggingface: 'Hugging Face',
  'github-copilot': 'GitHub Copilot',
  minimax: 'MiniMax',
  bedrock: 'Amazon Bedrock',
  'google-vertex': 'Google Vertex AI',
  'azure-openai': 'Azure OpenAI',
};

function displayProviderName(provider: string): string {
  return PROVIDER_DISPLAY[provider] ?? getProviderDisplayName(provider);
}

// --- Shared drag-and-drop components ---

type ModelPillProps = {
  label: string;
  modelKey: string;
  enabled: boolean;
  onAction: (key: string) => void;
  onDragStart: (ev: DragEvent, key: string) => void;
};

const ModelPill = ({ label, modelKey, enabled, onAction, onDragStart }: ModelPillProps) => (
  <span
    draggable
    onDragStart={(ev) => onDragStart(ev, modelKey)}
    className="inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium cursor-grab active:cursor-grabbing select-none border border-duck-dark/15 dark:border-foreground/15 bg-background/60 text-duck-dark/80 dark:text-foreground/80 hover:bg-duck-dark/5 dark:hover:bg-foreground/5 transition-colors"
  >
    {label}
    <button
      onClick={() => onAction(modelKey)}
      className="ml-0.5 p-0.5 rounded-full hover:bg-duck-dark/10 dark:hover:bg-foreground/10 cursor-pointer transition-colors"
    >
      {enabled ? <X className="h-3 w-3" /> : <Plus className="h-3 w-3" />}
    </button>
  </span>
);

type DropZoneProps = {
  label: string;
  children: React.ReactNode;
  onDrop: (key: string) => void;
};

const DropZone = ({ label, children, onDrop }: DropZoneProps) => {
  const [over, setOver] = useState(false);

  const handleDragOver = (ev: DragEvent) => {
    ev.preventDefault();
    setOver(true);
  };

  const handleDrop = (ev: DragEvent) => {
    ev.preventDefault();
    setOver(false);
    const key = ev.dataTransfer.getData('text/plain');
    if (key) onDrop(key);
  };

  return (
    <div className="grid gap-1.5">
      <span className="text-xs font-medium text-duck-dark/50 dark:text-foreground/50 uppercase tracking-wide">
        {label}
      </span>
      <div
        onDragOver={handleDragOver}
        onDragLeave={() => setOver(false)}
        onDrop={handleDrop}
        className={`min-h-[48px] p-2 rounded-lg border border-dashed transition-colors flex flex-wrap gap-1.5 ${over ? 'border-duck-teal bg-duck-teal/5' : 'border-duck-dark/15 dark:border-foreground/15'}`}
      >
        {children}
      </div>
    </div>
  );
};

// --- My Models Section (per-user hidden models) ---

function MyModelsSection() {
  const { user } = useAuth();
  const { settings, saveSettings } = useSettings();
  const allModels = usePiModels();
  const policyModels = useVisiblePiModels();
  const isAdmin = user?.role !== 'Member';
  const visibleModels = isAdmin ? allModels : policyModels;
  const [activeProvider, setActiveProvider] = useUserState<string>('my-models-provider', '');

  const hiddenModels = settings.chat.hiddenModels ?? [];

  const providerGroups = useMemo(() => {
    const groups: Record<string, { id: string; name: string }[]> = {};
    for (const m of visibleModels) {
      const provider = m.provider ?? 'Other';
      if (!groups[provider]) groups[provider] = [];
      groups[provider]!.push({ id: m.id, name: m.name });
    }
    return Object.entries(groups)
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([provider, models]) => ({ provider, models: models.sort((a, b) => a.name.localeCompare(b.name)) }));
  }, [visibleModels]);

  const providers = useMemo(() => providerGroups.map((g) => g.provider), [providerGroups]);
  const selected = providers.includes(activeProvider) ? activeProvider : (providers[0] ?? '');
  const currentGroup = providerGroups.find((g) => g.provider === selected);

  const { shown, hidden } = useMemo(() => {
    if (!currentGroup) return { shown: [], hidden: [] };
    const hiddenSet = new Set(hiddenModels);
    const s: { id: string; name: string; key: string }[] = [];
    const h: { id: string; name: string; key: string }[] = [];
    for (const m of currentGroup.models) {
      const key = `${currentGroup.provider}:${m.id}`;
      if (hiddenSet.has(key)) {
        h.push({ ...m, key });
      } else {
        s.push({ ...m, key });
      }
    }
    return { shown: s, hidden: h };
  }, [currentGroup, hiddenModels]);

  const saveHidden = useCallback(
    async (newHidden: string[]) => {
      const updated: UserSettings = {
        ...settings,
        chat: { ...settings.chat, hiddenModels: newHidden },
      };
      await saveSettings(updated);
    },
    [settings, saveSettings],
  );

  const hideModel = useCallback(
    async (key: string) => {
      if (hiddenModels.includes(key)) return;
      await saveHidden([...hiddenModels, key]);
    },
    [hiddenModels, saveHidden],
  );

  const showModel = useCallback(
    async (key: string) => {
      await saveHidden(hiddenModels.filter((k) => k !== key));
    },
    [hiddenModels, saveHidden],
  );

  const onDragStart = useCallback((ev: DragEvent, key: string) => {
    ev.dataTransfer.setData('text/plain', key);
    ev.dataTransfer.effectAllowed = 'move';
  }, []);

  if (providerGroups.length === 0) {
    return (
      <p className="text-sm text-duck-dark/40 dark:text-foreground/40">
        No models available. Configure API keys in Providers.
      </p>
    );
  }

  return (
    <div className="grid gap-4">
      <div className="flex flex-wrap items-center gap-1 border-b border-duck-dark/10 dark:border-foreground/10 pb-1">
        {providers.map((p) => (
          <button
            key={p}
            onClick={() => setActiveProvider(p)}
            className={`px-3 py-1.5 text-sm font-medium rounded-t-md transition-colors cursor-pointer ${
              p === selected
                ? 'bg-duck-teal/10 text-duck-teal border-b-2 border-duck-teal'
                : 'text-duck-dark/50 dark:text-foreground/50 hover:text-duck-dark dark:hover:text-foreground hover:bg-duck-dark/5 dark:hover:bg-foreground/5'
            }`}
          >
            {displayProviderName(p)}
          </button>
        ))}
      </div>

      <DropZone label="Visible" onDrop={showModel}>
        {shown.length === 0 && (
          <span className="text-xs text-duck-dark/30 dark:text-foreground/30 py-1">Drag models here to show</span>
        )}
        {shown.map((m) => (
          <ModelPill
            key={m.key}
            label={m.name}
            modelKey={m.key}
            enabled
            onAction={hideModel}
            onDragStart={onDragStart}
          />
        ))}
      </DropZone>

      <DropZone label="Hidden" onDrop={hideModel}>
        {hidden.length === 0 && (
          <span className="text-xs text-duck-dark/30 dark:text-foreground/30 py-1">All models visible</span>
        )}
        {hidden.map((m) => (
          <ModelPill
            key={m.key}
            label={m.name}
            modelKey={m.key}
            enabled={false}
            onAction={showModel}
            onDragStart={onDragStart}
          />
        ))}
      </DropZone>
    </div>
  );
}

// --- Member Models Section (system-wide policy, admin only) ---

function MemberModelsSection() {
  const client = useClient();
  const queryClient = useQueryClient();
  const { policy, savePolicy } = useAccessPolicy();
  const piModels = usePiModels();
  const [activeProvider, setActiveProvider] = useUserState<string>('member-models-provider', '');
  const [refreshing, setRefreshing] = useState(false);

  const refreshModels = async () => {
    setRefreshing(true);
    try {
      await client.post('/server-settings/pi-mono/local-providers/refresh');
      queryClient.invalidateQueries({ queryKey: ['PI_MODELS'] });
      toast.success('Models refreshed');
    } catch {
      toast.error('Failed to refresh models');
    } finally {
      setRefreshing(false);
    }
  };

  const enabledModels = policy.allowedModels;

  const providerGroups = useMemo(() => {
    const groups: Record<string, { id: string; name: string }[]> = {};
    for (const m of piModels) {
      const provider = m.provider ?? 'Other';
      if (!groups[provider]) groups[provider] = [];
      groups[provider]!.push({ id: m.id, name: m.name });
    }
    return Object.entries(groups)
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([provider, models]) => ({ provider, models: models.sort((a, b) => a.name.localeCompare(b.name)) }));
  }, [piModels]);

  const providers = useMemo(() => providerGroups.map((g) => g.provider), [providerGroups]);
  const selected = providers.includes(activeProvider) ? activeProvider : (providers[0] ?? '');
  const currentGroup = providerGroups.find((g) => g.provider === selected);

  const { enabled, disabled } = useMemo(() => {
    if (!currentGroup) return { enabled: [], disabled: [] };
    const allEnabled = enabledModels.length === 0;
    const enabledProviderSet = new Set(enabledModels.map((key) => key.split(':')[0]));
    const isNewProvider = !allEnabled && !enabledProviderSet.has(currentGroup.provider);
    const en: { id: string; name: string; key: string }[] = [];
    const dis: { id: string; name: string; key: string }[] = [];
    for (const m of currentGroup.models) {
      const key = `${currentGroup.provider}:${m.id}`;
      if (allEnabled || isNewProvider || enabledModels.includes(key)) {
        en.push({ ...m, key });
      } else {
        dis.push({ ...m, key });
      }
    }
    return { enabled: en, disabled: dis };
  }, [currentGroup, enabledModels]);

  const enableModel = useCallback(
    async (key: string) => {
      if (enabledModels.includes(key)) return;
      await savePolicy({ allowedModels: [...enabledModels, key] });
    },
    [enabledModels, savePolicy],
  );

  const disableModel = useCallback(
    async (key: string) => {
      let base = enabledModels;
      if (base.length === 0) {
        base = piModels.map((m) => modelKey(m));
      } else {
        const provider = key.split(':')[0]!;
        const knownProviders = new Set(base.map((k) => k.split(':')[0]));
        if (!knownProviders.has(provider)) {
          const newProviderKeys = piModels.filter((m) => m.provider === provider).map((m) => modelKey(m));
          base = [...base, ...newProviderKeys];
        }
      }
      await savePolicy({ allowedModels: base.filter((id) => id !== key) });
    },
    [enabledModels, piModels, savePolicy],
  );

  const onDragStart = useCallback((ev: DragEvent, key: string) => {
    ev.dataTransfer.setData('text/plain', key);
    ev.dataTransfer.effectAllowed = 'move';
  }, []);

  if (providerGroups.length === 0) {
    return (
      <p className="text-sm text-duck-dark/40 dark:text-foreground/40">
        No models available. Configure API keys in Providers.
      </p>
    );
  }

  return (
    <div className="grid gap-4">
      <div className="flex flex-wrap items-center gap-1 border-b border-duck-dark/10 dark:border-foreground/10 pb-1">
        {providers.map((p) => (
          <button
            key={p}
            onClick={() => setActiveProvider(p)}
            className={`px-3 py-1.5 text-sm font-medium rounded-t-md transition-colors cursor-pointer ${
              p === selected
                ? 'bg-duck-teal/10 text-duck-teal border-b-2 border-duck-teal'
                : 'text-duck-dark/50 dark:text-foreground/50 hover:text-duck-dark dark:hover:text-foreground hover:bg-duck-dark/5 dark:hover:bg-foreground/5'
            }`}
          >
            {displayProviderName(p)}
          </button>
        ))}
        <button
          type="button"
          onClick={refreshModels}
          disabled={refreshing}
          className="ml-auto p-1.5 rounded hover:bg-duck-teal/10 cursor-pointer transition-colors disabled:opacity-30"
          title="Refresh models from providers"
        >
          <RefreshCw className={`h-3.5 w-3.5 text-duck-teal ${refreshing ? 'animate-spin' : ''}`} />
        </button>
      </div>

      <DropZone label="Enabled" onDrop={enableModel}>
        {enabled.length === 0 && (
          <span className="text-xs text-duck-dark/30 dark:text-foreground/30 py-1">Drag models here to enable</span>
        )}
        {enabled.map((m) => (
          <ModelPill
            key={m.key}
            label={m.name}
            modelKey={m.key}
            enabled
            onAction={disableModel}
            onDragStart={onDragStart}
          />
        ))}
      </DropZone>

      <DropZone label="Disabled" onDrop={disableModel}>
        {disabled.length === 0 && (
          <span className="text-xs text-duck-dark/30 dark:text-foreground/30 py-1">All models enabled</span>
        )}
        {disabled.map((m) => (
          <ModelPill
            key={m.key}
            label={m.name}
            modelKey={m.key}
            enabled={false}
            onAction={enableModel}
            onDragStart={onDragStart}
          />
        ))}
      </DropZone>
    </div>
  );
}

// --- Chat Defaults Section (commented out for future use) ---

// function ChatDefaultsSection() {
//   const { settings, saveSettings } = useSettings();
//   const piModels = useVisiblePiModels();
//   const [isSaving, setIsSaving] = useState(false);
//   const [model, setModel] = useState<string | null>(settings.chat.defaultModel);
//   const [systemPrompt, setSystemPrompt] = useState(settings.chat.systemPrompt);
//   const [temperature, setTemperature] = useState(settings.chat.temperature);
//   const [defaultPwd, setDefaultPwd] = useState(settings.chat.defaultPwd);
//   // ... full implementation for future use
// }

// --- Build groups based on role ---

function useAISettingsGroups(): SettingsSectionGroup[] {
  const { user } = useAuth();
  const isAdmin = user?.role !== 'Member';

  return useMemo(() => {
    const modelsGroup: SettingsSectionGroup = {
      label: 'Models',
      icon: Eye,
      sections: [
        {
          key: 'my-models',
          icon: Eye,
          title: 'My Models',
          description: 'Show or hide models for yourself',
          content: <MyModelsSection />,
        },
        ...(isAdmin
          ? [
              {
                key: 'member-models',
                icon: Eye,
                title: 'Member Models',
                description: 'Enable or disable models for members',
                content: <MemberModelsSection />,
              },
            ]
          : []),
      ],
    };

    const defaultsGroup: SettingsSectionGroup = {
      label: 'Defaults',
      icon: Sparkles,
      sections: [
        {
          key: 'default-models',
          icon: Sparkles,
          title: 'Default Models',
          description: 'Default models for chat, projects, and tasks',
          content: <AIModels />,
        },
      ],
    };

    if (isAdmin) {
      const providersGroup: SettingsSectionGroup = {
        label: 'Providers',
        icon: Terminal,
        sections: [
          {
            key: 'ai-harnesses',
            icon: Terminal,
            title: 'Providers',
            description: 'Remote and local AI providers',
            content: <AIHarnessesSection />,
          },
        ],
      };
      return [providersGroup, modelsGroup, defaultsGroup];
    }

    return [modelsGroup, defaultsGroup];
  }, [isAdmin]);
}

const layout: LayoutNode = {
  type: 'group',
  id: 'ai-root',
  direction: 'horizontal',
  children: [
    { node: { type: 'panel', id: 'ai-left', appType: null }, size: 20 },
    { node: { type: 'panel', id: 'ai-right', appType: null }, size: 80 },
  ],
};

export const AISettings = () => {
  const groups = useAISettingsGroups();

  const { Sidebar, Content } = useMemo(
    () =>
      createSettingsPanelComponents({
        globalKey: 'AI_SETTINGS_SELECTED',
        sidebarIcon: Bot,
        sidebarLabel: 'AI',
        groups,
      }),
    [groups],
  );

  const panelComponents: PanelComponents = useMemo(
    () => ({
      'ai-left': Sidebar,
      'ai-right': Content,
    }),
    [Sidebar, Content],
  );

  return (
    <div className="h-full w-full pt-2">
      <WorkspaceLayout layout={layout} onLayoutChange={() => {}} components={panelComponents} />
    </div>
  );
};
