import { useCallback, useEffect, useRef, useState } from 'react';
import { authenticatedFetch } from '../../../utils/api';
import { CLAUDE_MODELS, CODEX_MODELS, CURSOR_MODELS, GEMINI_MODELS } from '../../../../shared/modelConstants';
import type { PendingPermissionRequest, PermissionMode } from '../types/types';
import type { ProjectSession, LLMProvider } from '../../../types/app';

type ModelOption = { value: string; label: string };

const getPermissionModesForProvider = (provider: LLMProvider): PermissionMode[] => {
  if (provider === 'codex') {
    return ['default', 'acceptEdits', 'bypassPermissions'];
  }
  if (provider === 'claude') {
    return ['default', 'auto', 'acceptEdits', 'bypassPermissions', 'plan'];
  }
  return ['default', 'acceptEdits', 'bypassPermissions', 'plan'];
};

// Per-session model persistence keys.
// Global key (`${provider}-model`) holds the "last used" default for NEW sessions.
// Session key (`${provider}-model-${sessionId}`) pins the model for that session.
const globalModelKey = (provider: string) => `${provider}-model`;
const sessionModelKey = (provider: string, sessionId: string) => `${provider}-model-${sessionId}`;

interface UseChatProviderStateArgs {
  selectedSession: ProjectSession | null;
}

export function useChatProviderState({ selectedSession }: UseChatProviderStateArgs) {
  const [permissionMode, setPermissionMode] = useState<PermissionMode>('default');
  const [pendingPermissionRequests, setPendingPermissionRequests] = useState<PendingPermissionRequest[]>([]);
  const [provider, setProvider] = useState<LLMProvider>(() => {
    return (localStorage.getItem('selected-provider') as LLMProvider) || 'claude';
  });

  // Initial model values: prefer the per-session pin if a session is already
  // selected on mount, otherwise the global last-used default.
  const initialModel = (p: string, fallback: string) => {
    if (selectedSession?.id) {
      const pinned = localStorage.getItem(sessionModelKey(p, selectedSession.id));
      if (pinned) return pinned;
    }
    return localStorage.getItem(globalModelKey(p)) || fallback;
  };

  const [cursorModel, setCursorModelState] = useState<string>(() => initialModel('cursor', CURSOR_MODELS.DEFAULT));
  const [claudeModel, setClaudeModelState] = useState<string>(() => initialModel('claude', CLAUDE_MODELS.DEFAULT));
  const [codexModel, setCodexModelState] = useState<string>(() => initialModel('codex', CODEX_MODELS.DEFAULT));
  const [geminiModel, setGeminiModelState] = useState<string>(() => initialModel('gemini', GEMINI_MODELS.DEFAULT));

  // Live model lists — fall back to static constants until API responds
  const [claudeModelOptions, setClaudeModelOptions] = useState<ModelOption[]>(CLAUDE_MODELS.OPTIONS);
  const [codexModelOptions] = useState<ModelOption[]>(CODEX_MODELS.OPTIONS);
  const [geminiModelOptions] = useState<ModelOption[]>(GEMINI_MODELS.OPTIONS);
  const [cursorModelOptions] = useState<ModelOption[]>(CURSOR_MODELS.OPTIONS);

  // Persisted setters: write BOTH the per-session pin (if a session is active)
  // and the global last-used default, then update state.
  const makePersistedSetter = useCallback(
    (p: string, setState: (v: string) => void) => (value: string) => {
      setState(value);
      localStorage.setItem(globalModelKey(p), value);
      if (selectedSession?.id) {
        localStorage.setItem(sessionModelKey(p, selectedSession.id), value);
      }
    },
    [selectedSession?.id],
  );

  const setCursorModel = useCallback(makePersistedSetter('cursor', setCursorModelState), [makePersistedSetter]);
  const setClaudeModel = useCallback(makePersistedSetter('claude', setClaudeModelState), [makePersistedSetter]);
  const setCodexModel = useCallback(makePersistedSetter('codex', setCodexModelState), [makePersistedSetter]);
  const setGeminiModel = useCallback(makePersistedSetter('gemini', setGeminiModelState), [makePersistedSetter]);

  // When the selected session changes, load each provider's pinned model for
  // that session (falling back to the global default). This is what stops a
  // model picked in session A from leaking into session B.
  useEffect(() => {
    if (!selectedSession?.id) {
      return;
    }
    const load = (p: string, setState: (v: string) => void, fallback: string) => {
      const pinned = localStorage.getItem(sessionModelKey(p, selectedSession.id));
      const next = pinned || localStorage.getItem(globalModelKey(p)) || fallback;
      setState(next);
    };
    load('cursor', setCursorModelState, CURSOR_MODELS.DEFAULT);
    load('claude', setClaudeModelState, CLAUDE_MODELS.DEFAULT);
    load('codex', setCodexModelState, CODEX_MODELS.DEFAULT);
    load('gemini', setGeminiModelState, GEMINI_MODELS.DEFAULT);
  }, [selectedSession?.id]);

  // Fetch live Claude model list and validate the current claude model
  useEffect(() => {
    authenticatedFetch('/api/models')
      .then((res) => {
        if (!res.ok) return;
        return res.json();
      })
      .then((data) => {
        if (!Array.isArray(data?.claude) || data.claude.length === 0) return;
        const options: ModelOption[] = data.claude;
        setClaudeModelOptions(options);

        setClaudeModelState((current) => {
          const valid = options.some((o) => o.value === current);
          if (valid) return current;
          const fallback = options[0]?.value ?? CLAUDE_MODELS.DEFAULT;
          localStorage.setItem(globalModelKey('claude'), fallback);
          if (selectedSession?.id) {
            localStorage.setItem(sessionModelKey('claude', selectedSession.id), fallback);
          }
          return fallback;
        });
      })
      .catch(() => {
        // Static fallback already in place
      });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const lastProviderRef = useRef(provider);

  useEffect(() => {
    if (!selectedSession?.id) {
      return;
    }

    const savedMode = localStorage.getItem(`permissionMode-${selectedSession.id}`) as PermissionMode | null;
    const validModes = getPermissionModesForProvider(provider);
    setPermissionMode(savedMode && validModes.includes(savedMode) ? savedMode : 'default');
  }, [selectedSession?.id, provider]);

  useEffect(() => {
    if (!selectedSession?.__provider || selectedSession.__provider === provider) {
      return;
    }

    setProvider(selectedSession.__provider);
    localStorage.setItem('selected-provider', selectedSession.__provider);
  }, [provider, selectedSession]);

  useEffect(() => {
    if (lastProviderRef.current === provider) {
      return;
    }
    setPendingPermissionRequests([]);
    lastProviderRef.current = provider;
  }, [provider]);

  useEffect(() => {
    setPendingPermissionRequests((previous) =>
      previous.filter((request) => !request.sessionId || request.sessionId === selectedSession?.id),
    );
  }, [selectedSession?.id]);

  useEffect(() => {
    if (provider !== 'cursor') {
      return;
    }

    authenticatedFetch('/api/cursor/config')
      .then((response) => response.json())
      .then((data) => {
        if (!data.success || !data.config?.model?.modelId) {
          return;
        }

        const modelId = data.config.model.modelId as string;
        if (!localStorage.getItem(globalModelKey('cursor'))) {
          setCursorModelState(modelId);
        }
      })
      .catch((error) => {
        console.error('Error loading Cursor config:', error);
      });
  }, [provider]);

  const cyclePermissionMode = useCallback(() => {
    const modes = getPermissionModesForProvider(provider);

    const currentIndex = modes.indexOf(permissionMode);
    const nextIndex = (currentIndex + 1) % modes.length;
    const nextMode = modes[nextIndex];
    setPermissionMode(nextMode);

    if (selectedSession?.id) {
      localStorage.setItem(`permissionMode-${selectedSession.id}`, nextMode);
    }
  }, [permissionMode, provider, selectedSession?.id]);

  return {
    provider,
    setProvider,
    cursorModel,
    setCursorModel,
    claudeModel,
    setClaudeModel,
    codexModel,
    setCodexModel,
    geminiModel,
    setGeminiModel,
    claudeModelOptions,
    codexModelOptions,
    geminiModelOptions,
    cursorModelOptions,
    permissionMode,
    setPermissionMode,
    pendingPermissionRequests,
    setPendingPermissionRequests,
    cyclePermissionMode,
  };
}
