import { useEffect, useMemo, useRef, useState } from 'react';
import { Check, ChevronDown, RefreshCw, Shield, Brain, Bird, Bot, Dog, Terminal as TermIcon } from 'lucide-react';
import { ClaudeIcon, OpenCodeIcon } from '@/components/icons/ProviderIcons';
import { api } from '@/api/client';
import type { AgentSessionConfig, ProviderSnapshotEntry, AgentCommand } from '@/api/types';
import { useProviderSnapshot, refreshProviderSnapshot } from '@/hooks/useProviderSnapshot';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { BottomSheet } from '@/components/BottomSheet';
import { useViewport } from '@/hooks/useViewport';
import { cn } from '@/lib/utils';

const PREFS_KEY = 'boocode.coder.agent-prefs';


type ProviderPrefs = Record<string, {
  model: string;
  modeId: string | null;
  thinkingOptionId: string | null;
}>;

function loadPrefs(): ProviderPrefs {
  try {
    const raw = localStorage.getItem(PREFS_KEY);
    return raw ? (JSON.parse(raw) as ProviderPrefs) : {};
  } catch {
    return {};
  }
}

function savePrefs(prefs: ProviderPrefs): void {
  localStorage.setItem(PREFS_KEY, JSON.stringify(prefs));
}

function defaultsForProvider(entry: ProviderSnapshotEntry): AgentSessionConfig {
  const model =
    entry.models.find((m) => m.isDefault)?.id ??
    entry.models[0]?.id ??
    '';
  const selectedModel = entry.models.find((m) => m.id === model);
  const modeId = entry.defaultModeId ?? entry.modes[0]?.id ?? null;
  const thinkingOptionId =
    selectedModel?.defaultThinkingOptionId ??
    selectedModel?.thinkingOptions?.find((t) => t.isDefault)?.id ??
    selectedModel?.thinkingOptions?.[0]?.id ??
    null;

  return {
    provider: entry.name,
    model,
    modeId,
    thinkingOptionId,
  };
}

function resolveConfig(
  entry: ProviderSnapshotEntry,
  prefs: ProviderPrefs,
): AgentSessionConfig {
  const saved = prefs[entry.name];
  const base = defaultsForProvider(entry);

  const model =
    saved?.model && entry.models.some((m) => m.id === saved.model)
      ? saved.model
      : base.model;

  const selectedModel = entry.models.find((m) => m.id === model);
  const modeId =
    saved?.modeId && entry.modes.some((m) => m.id === saved.modeId)
      ? saved.modeId
      : base.modeId;

  const thinkingOptions = selectedModel?.thinkingOptions ?? [];
  const thinkingOptionId =
    saved?.thinkingOptionId &&
    thinkingOptions.some((t) => t.id === saved.thinkingOptionId)
      ? saved.thinkingOptionId
      : base.thinkingOptionId;

  return { provider: entry.name, model, modeId, thinkingOptionId };
}

interface PickerProps {
  label: string;
  value: string;
  disabled?: boolean;
  options: Array<{ id: string; label: string }>;
  onPick: (id: string) => void;
  icon?: React.ReactNode;
}

function CompactPicker({ label, value, disabled, options, onPick, icon }: PickerProps) {
  const { isMobile } = useViewport();
  const [open, setOpen] = useState(false);
  const currentLabel = options.find((o) => o.id === value)?.label ?? (value || label);

  const list = (
    <div className="py-1">
      {options.map((o) => (
        <button
          key={o.id}
          type="button"
          onClick={() => {
            onPick(o.id);
            setOpen(false);
          }}
          className="w-full text-left flex items-center gap-2 font-mono text-xs px-2 py-1.5 hover:bg-accent rounded"
        >
          <Check className={cn('size-3 shrink-0', o.id === value ? 'opacity-100' : 'opacity-0')} />
          <span className="truncate">{o.label}</span>
        </button>
      ))}
    </div>
  );

  if (isMobile) {
    return (
      <>
        <button
          type="button"
          disabled={disabled}
          onClick={() => setOpen(true)}
          aria-label={`${label}: ${currentLabel}`}
          className="inline-flex items-center gap-1 min-h-[44px] px-1.5 rounded text-xs text-muted-foreground hover:text-foreground disabled:opacity-40"
        >
          {icon}
          <span className="truncate max-w-[120px]">{currentLabel}</span>
          <ChevronDown className="size-3 opacity-70 shrink-0" />
        </button>
        <BottomSheet open={open} onClose={() => setOpen(false)} title={label}>
          <div className="px-2">{list}</div>
        </BottomSheet>
      </>
    );
  }

  return (
    <DropdownMenu open={open} onOpenChange={setOpen}>
      <DropdownMenuTrigger asChild>
        <button
          type="button"
          disabled={disabled}
          className="text-xs text-muted-foreground hover:text-foreground flex items-center gap-1 px-1.5 py-0.5 rounded hover:bg-muted/60 disabled:opacity-40"
        >
          {icon}
          <span className="truncate max-w-[180px]">{currentLabel}</span>
          <ChevronDown className="size-3 opacity-70 shrink-0" />
        </button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="start" className="max-h-64 overflow-y-auto min-w-[160px]">
        {options.map((o) => (
          <DropdownMenuItem key={o.id} onSelect={() => onPick(o.id)} className="text-xs">
            <Check className={cn('size-3 shrink-0', o.id === value ? 'opacity-100' : 'opacity-0')} />
            {o.label}
          </DropdownMenuItem>
        ))}
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

interface Props {
  projectPath?: string;
  value: AgentSessionConfig;
  onChange: (next: AgentSessionConfig) => void;
  onProviderCommandsChange?: (commands: AgentCommand[]) => void;
  connected?: boolean;
}

export function AgentComposerBar({ projectPath, value, onChange, onProviderCommandsChange, connected }: Props) {
  const allEntries = useProviderSnapshot(projectPath);
  const entries = useMemo(
    () => allEntries?.filter((e) => e.installed && e.status !== 'error') ?? null,
    [allEntries],
  );
  const [refreshing, setRefreshing] = useState(false);

  const hydratedRef = useRef(false);

  useEffect(() => {
    hydratedRef.current = false;
  }, [projectPath]);

  useEffect(() => {
    if (!entries?.length || hydratedRef.current) return;
    hydratedRef.current = true;
    const prefs = loadPrefs();
    const entry =
      entries.find((e) => e.name === value.provider) ??
      entries.find((e) => e.name === 'boocode') ??
      entries[0];
    if (!entry) return;
    onChange(resolveConfig(entry, prefs));
  }, [entries, onChange, value.provider]);

  const currentEntry = useMemo(
    () => entries?.find((e) => e.name === value.provider),
    [entries, value.provider],
  );

  const currentModel = useMemo(
    () => currentEntry?.models.find((m) => m.id === value.model),
    [currentEntry, value.model],
  );

  const thinkingOptions = currentModel?.thinkingOptions ?? [];

  useEffect(() => {
    onProviderCommandsChange?.(currentEntry?.commands ?? []);
  }, [currentEntry, onProviderCommandsChange]);

  function persist(next: AgentSessionConfig): void {
    const prefs = loadPrefs();
    prefs[next.provider] = {
      model: next.model,
      modeId: next.modeId,
      thinkingOptionId: next.thinkingOptionId,
    };
    savePrefs(prefs);
    onChange(next);
  }

  function pickProvider(name: string): void {
    const entry = entries?.find((e) => e.name === name);
    if (!entry) return;
    persist(resolveConfig(entry, loadPrefs()));
  }

  function pickModel(model: string): void {
    const entry = currentEntry;
    if (!entry) return;
    const selected = entry.models.find((m) => m.id === model);
    const thinkingOptionId =
      selected?.defaultThinkingOptionId ??
      selected?.thinkingOptions?.find((t) => t.isDefault)?.id ??
      selected?.thinkingOptions?.[0]?.id ??
      null;
    persist({ ...value, model, thinkingOptionId });
  }

  async function handleRefresh(): Promise<void> {
    setRefreshing(true);
    try {
      await api.coder.refreshProviders();
      await refreshProviderSnapshot(projectPath);
    } finally {
      setRefreshing(false);
    }
  }

  if (!entries) {
    return (
      <div className="text-xs text-muted-foreground px-2 py-1">Loading agents…</div>
    );
  }

  const providerIcon = (name: string) => {
    switch (name) {
      case 'claude': return <ClaudeIcon size={13} className="shrink-0" />;
      case 'opencode': return <OpenCodeIcon size={13} className="shrink-0" />;
      case 'goose': return <Bird size={13} className="shrink-0" />;
      case 'qwen': return <TermIcon size={13} className="shrink-0" />;
      default: return <Dog size={13} className="shrink-0" />;
    }
  };

  const providerOptions = entries.map((e) => ({ id: e.name, label: e.label }));
  const modeOptions = (currentEntry?.modes ?? []).map((m) => ({ id: m.id, label: m.label }));
  const modelOptions = (currentEntry?.models ?? []).map((m) => ({ id: m.id, label: m.label }));
  const thinkingOpts = thinkingOptions.map((t) => ({ id: t.id, label: t.label }));

  return (
    <div className="flex flex-wrap items-center gap-1 px-2 py-1 border-b border-border bg-muted/20 shrink-0">
      <CompactPicker
        label="Provider"
        value={value.provider}
        options={providerOptions}
        onPick={pickProvider}
        icon={providerIcon(value.provider)}
      />
      <CompactPicker
        label="Mode"
        value={value.modeId ?? ''}
        disabled={modeOptions.length === 0}
        options={modeOptions}
        onPick={(modeId) => persist({ ...value, modeId })}
        icon={<Shield className="size-3 shrink-0" />}
      />
      <CompactPicker
        label="Model"
        value={value.model}
        disabled={modelOptions.length === 0}
        options={modelOptions}
        onPick={pickModel}
        icon={<Bot size={13} className="shrink-0" />}
      />
      {thinkingOpts.length > 0 && (
        <CompactPicker
          label="Thinking"
          value={value.thinkingOptionId ?? ''}
          options={thinkingOpts}
          onPick={(thinkingOptionId) => persist({ ...value, thinkingOptionId })}
          icon={<Brain className="size-3 shrink-0" />}
        />
      )}
      {connected !== undefined && (
        <span
          className={cn('inline-block w-1.5 h-1.5 rounded-full shrink-0 ml-auto', connected ? 'bg-green-500' : 'bg-red-500')}
          title={connected ? 'Connected' : 'Disconnected'}
        />
      )}
      <button
        type="button"
        onClick={() => void handleRefresh()}
        disabled={refreshing}
        className={cn('inline-flex items-center justify-center size-7 max-md:min-h-[44px] max-md:min-w-[44px] rounded text-muted-foreground hover:text-foreground disabled:opacity-40', connected === undefined && 'ml-auto')}
        aria-label="Refresh provider list"
        title="Refresh providers"
      >
        <RefreshCw className={cn('size-3.5', refreshing && 'animate-spin')} />
      </button>
    </div>
  );
}
