import { useState, useEffect, useCallback } from 'react';
import type { ReactNode } from 'react';
import { useClient } from 'hooks/useClient';
import { useAuth } from 'hooks/useAuth';
import { usePanelChannel } from 'hooks/usePanelChannel';
import { useWorkspace } from '../../components/Workspace';
import type { ProjectDefinition } from '../../components/Workspace';
import { useDashboardState } from 'state/useDashboardState';
import { PreviewContext } from './PreviewContext';

type DevServerResponse = { url: string; port: number };
type DevServerStatus = { running: boolean; url?: string; port?: number };

type PreviewProviderProps = { panelId: string; children: ReactNode };

export const PreviewProvider = ({ panelId, children }: PreviewProviderProps) => {
  const { cwd } = useWorkspace();
  const client = useClient();
  const { user } = useAuth();
  const { value: projects } = useDashboardState<ProjectDefinition[]>('projects', []);
  const [selectedSlug, setSelectedSlug] = useState<string | null>(null);
  const [url, setUrl] = useState<string | null>(null);
  const [port, setPort] = useState<number | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [iframeKey, setIframeKey] = useState(0);
  const [stopped, setStopped] = useState(false);

  const cwdSlug = extractSlug(cwd);
  const slug = cwdSlug ?? selectedSlug;

  const startServer = useCallback(
    async (targetSlug: string) => {
      setLoading(true);
      setError(null);
      setStopped(false);
      try {
        const res = await client.post<DevServerResponse>('/dev-server/start', { slug: targetSlug });
        const token = client.token;
        setUrl(token ? `${res.url}?token=${encodeURIComponent(token)}` : res.url);
        setPort(res.port);
      } catch (err: unknown) {
        const msg =
          err && typeof err === 'object' && 'message' in err ? String(err.message) : 'Failed to start dev server';
        setError(msg);
      } finally {
        setLoading(false);
      }
    },
    [client],
  );

  const stopServer = useCallback(async () => {
    if (!slug) return;
    try {
      await client.post('/dev-server/stop', { slug });
    } catch {
      // ignore
    }
    setUrl(null);
    setPort(null);
    setStopped(true);
  }, [slug, client]);

  const restartServer = useCallback(async () => {
    if (!slug) return;
    try {
      await client.post('/dev-server/stop', { slug });
    } catch {
      // ignore — server may not be running
    }
    startServer(slug);
  }, [slug, client, startServer]);

  const refresh = useCallback(() => setIframeKey((k) => k + 1), []);

  const clearError = useCallback(() => {
    setError(null);
    setSelectedSlug(null);
  }, []);

  useEffect(() => {
    if (!slug) return;
    setUrl(null);
    setPort(null);
    setError(null);

    let cancelled = false;
    const check = async () => {
      try {
        const status = await client.get<DevServerStatus>(`/dev-server/status?slug=${encodeURIComponent(slug)}`);
        if (cancelled) return;
        if (status.running && status.url) {
          const token = client.token;
          setUrl(token ? `${status.url}?token=${encodeURIComponent(token)}` : status.url);
          setPort(status.port ?? null);
        } else {
          startServer(slug);
        }
      } catch {
        if (!cancelled) startServer(slug);
      }
    };
    check();

    return () => {
      cancelled = true;
    };
  }, [slug]);

  // Poll status while a server is supposedly running — auto-restart if it died (e.g. idle timeout)
  useEffect(() => {
    if (!slug || !url) return;

    const interval = setInterval(async () => {
      try {
        const status = await client.get<DevServerStatus>(`/dev-server/status?slug=${encodeURIComponent(slug)}`);
        if (!status.running) {
          startServer(slug);
        }
      } catch {
        // ignore transient failures
      }
    }, 30_000);

    return () => clearInterval(interval);
  }, [slug, url]);

  // Listen for preview:refresh signal from chat panel (triggers after tool-call turns)
  const [refreshSignal] = usePanelChannel<number>('preview:refresh', 0);
  useEffect(() => {
    if (refreshSignal && slug && url) restartServer();
  }, [refreshSignal]);

  return (
    <PreviewContext
      value={{
        slug,
        cwdSlug,
        url,
        port,
        loading,
        error,
        stopped,
        iframeKey,
        projects,
        startServer,
        stopServer,
        restartServer,
        refresh,
        setSelectedSlug,
        clearError,
      }}
    >
      {children}
    </PreviewContext>
  );
};

const extractSlug = (cwd: string): string | null => {
  if (!cwd || cwd === '~') return null;
  const match = cwd.match(/^\/Projects\/(.+?)(?:\/|$)/);
  return match?.[1] ?? null;
};
