import { useState, useEffect, useRef, useCallback } from "react";
import type { Config } from "../types.js";
import {
  checkHealth,
  getSession,
  getServerConfig,
  getIndexStatus,
  getHistory,
  createSession,
  type ServerConfig,
  type HistoryMessage,
} from "../api/client.js";
import { INDEX_STATUS_POLL_MS, INDEX_DONE_HIDE_MS } from "../lib/constants.js";

interface IndexStatus {
  indexing: boolean;
  progress: { total: number; done: number; status: string };
  reembedding?: boolean;
  reembed_progress?: { total: number; done: number } | null;
}

export function useSession(config: Config) {
  const [sessionId, setSessionId] = useState<string | null>(null);
  const [sessionName, setSessionName] = useState<string | null>(null);
  const [sources, setSources] = useState<string[]>([]);
  const [skipApprovals, setSkipApprovals] = useState(false);
  const [serverConnected, setServerConnected] = useState(false);
  const [serverVersion, setServerVersion] = useState<string | null>(null);
  const [serverConfig, setServerConfig] = useState<ServerConfig | null>(null);
  const [indexStatus, setIndexStatus] = useState<IndexStatus | null>(null);
  const [history, setHistory] = useState<HistoryMessage[]>([]);
  const initRef = useRef(false);

  const initializeSession = useCallback(async () => {
    try {
      const health = await checkHealth(config);
      if (!health.ok) return false;

      const [session, configData, idxStatus, historyData] = await Promise.all([
        getSession(config),
        getServerConfig(config),
        getIndexStatus(config).catch(() => null),
        getHistory(config).catch(() => ({ messages: [] })),
      ]);

      setSessionId(session.session_id);
      setSessionName(session.name ?? null);
      setSources(session.sources);
      setServerConfig(configData);
      setHistory(historyData.messages);
      setServerConnected(true);
      setServerVersion(health.version);

      if (idxStatus?.indexing || idxStatus?.reembedding) {
        setIndexStatus(idxStatus);
      }
      return true;
    } catch {
      return false;
    }
  }, [config]);

  // Initial connection with retry
  useEffect(() => {
    if (initRef.current) return;
    initRef.current = true;

    let cancelled = false;
    async function init() {
      for (let i = 0; i < 30 && !cancelled; i++) {
        if (await initializeSession()) return;
        await new Promise((r) => setTimeout(r, 1000));
      }
    }
    init();
    return () => { cancelled = true; };
  }, [initializeSession]);

  // Heartbeat: detect server going down, reconnect when it comes back
  useEffect(() => {
    if (!initRef.current) return;

    const intervalMs = serverConnected ? 10000 : 3000;
    const poll = setInterval(async () => {
      const health = await checkHealth(config);
      if (health.ok && !serverConnected) {
        await initializeSession();
      } else if (!health.ok && serverConnected) {
        setServerConnected(false);
      }
    }, intervalMs);

    return () => clearInterval(poll);
  }, [serverConnected, config, initializeSession]);

  useEffect(() => {
    if (!serverConnected) return;

    const isActive = indexStatus?.indexing || indexStatus?.reembedding;

    if (indexStatus && !isActive) {
      const timeout = setTimeout(() => {
        setIndexStatus(null);
      }, INDEX_DONE_HIDE_MS);
      return () => clearTimeout(timeout);
    }

    if (!isActive) return;

    const interval = setInterval(async () => {
      try {
        const status = await getIndexStatus(config);
        setIndexStatus(status);
      } catch {
        // Ignore errors
      }
    }, INDEX_STATUS_POLL_MS);

    return () => clearInterval(interval);
  }, [serverConnected, indexStatus, config]);

  const refreshIndexStatus = async () => {
    try {
      const status = await getIndexStatus(config);
      setIndexStatus(status);
    } catch {
      // Ignore errors
    }
  };

  const updateSessionInfo = (info: { session_id: string; sources?: string[]; session_name?: string }) => {
    setSessionId(info.session_id);
    if (info.sources !== undefined) {
      setSources(info.sources);
    }
    if (info.session_name !== undefined) {
      setSessionName(info.session_name || null);
    }
  };

  const toggleSkipApprovals = () => {
    setSkipApprovals((prev) => !prev);
  };

  const updateServerConfig = (patch: Partial<ServerConfig>) => {
    setServerConfig((prev) => prev && { ...prev, ...patch });
  };

  const sessionIdRef = useRef(sessionId);
  sessionIdRef.current = sessionId;
  const sessionNameRef = useRef(sessionName);
  sessionNameRef.current = sessionName;

  const switchSession = useCallback(async (targetSessionId: string): Promise<{ history: HistoryMessage[] } | null> => {
    const prevId = sessionIdRef.current;
    const prevName = sessionNameRef.current;
    setSessionId(targetSessionId);
    try {
      const [session, historyData] = await Promise.all([
        getSession(config, targetSessionId),
        getHistory(config, targetSessionId).catch(() => ({ messages: [] })),
      ]);
      setSessionId(session.session_id);
      setSessionName(session.name ?? null);
      setSources(session.sources);
      setHistory(historyData.messages);
      return { history: historyData.messages };
    } catch {
      setSessionId(prevId);
      setSessionName(prevName);
      return null;
    }
  }, [config]);

  const createNewSession = useCallback(async (name?: string): Promise<string | null> => {
    try {
      const result = await createSession(config, name);
      setSessionId(result.session_id);
      setSessionName(result.name ?? null);
      setSources([]);
      setHistory([]);
      return result.session_id;
    } catch {
      return null;
    }
  }, [config]);

  return {
    sessionId,
    sessionName,
    sources,
    skipApprovals,
    serverConnected,
    serverVersion,
    serverConfig,
    indexStatus,
    history,
    refreshIndexStatus,
    updateSessionInfo,
    toggleSkipApprovals,
    updateServerConfig,
    switchSession,
    createNewSession,
  };
}
