"use client";

import {
  createContext,
  useContext,
  useEffect,
  useMemo,
  useState,
  type ReactNode
} from "react";
import type {
  IslandflowAiReasoningEffort,
  IslandflowAiState,
  IslandflowAiTaskRequest
} from "@islandflow/types";

type DesktopAiBridge = {
  ai: {
    getState: () => Promise<IslandflowAiState>;
    loginWithBrowser: () => Promise<void>;
    loginWithDeviceCode: () => Promise<void>;
    cancelLogin: () => Promise<void>;
    logout: () => Promise<void>;
    updatePreferences: (
      next: Partial<{ model: string | null; reasoningEffort: IslandflowAiReasoningEffort | null }>
    ) => Promise<void>;
    runTask: (request: IslandflowAiTaskRequest) => Promise<{ taskId: string }>;
    cancelTask: (taskId: string) => Promise<void>;
    subscribe: (listener: (state: IslandflowAiState) => void) => () => void;
  };
};

type DesktopAiRuntime = {
  shellAvailable: boolean;
  bridgeAvailable: boolean;
  bridge: DesktopAiBridge | null;
};

const BROWSER_RUNTIME: DesktopAiRuntime = {
  shellAvailable: false,
  bridgeAvailable: false,
  bridge: null
};

declare global {
  interface Window {
    islandflowDesktopRuntime?: {
      app?: string | null;
      shell?: string | null;
    };
    islandflowDesktop?: DesktopAiBridge;
  }
}

type DesktopAiContextValue = {
  bridgeAvailable: boolean;
  shellAvailable: boolean;
  state: IslandflowAiState;
  loginWithBrowser: () => Promise<void>;
  loginWithDeviceCode: () => Promise<void>;
  cancelLogin: () => Promise<void>;
  logout: () => Promise<void>;
  updatePreferences: (
    next: Partial<{ model: string | null; reasoningEffort: IslandflowAiReasoningEffort | null }>
  ) => Promise<void>;
  runTask: (request: IslandflowAiTaskRequest) => Promise<{ taskId: string }>;
  cancelTask: (taskId: string) => Promise<void>;
};

const BRIDGE_POLL_INTERVAL_MS = 250;
const BRIDGE_POLL_MAX_ATTEMPTS = 20;
const ELECTRON_USER_AGENT_PATTERN = /\b(?:Electron|IslandflowDesktop)\/\S+/i;

export const detectDesktopShell = (userAgent: string | null | undefined): boolean =>
  Boolean(userAgent && ELECTRON_USER_AGENT_PATTERN.test(userAgent));

export const detectDesktopRuntimeMarker = (
  runtime:
    | {
        app?: string | null;
        shell?: string | null;
      }
    | null
    | undefined
): boolean => runtime?.app === "islandflow" && runtime.shell === "electron";

export const resolveDesktopAiRuntime = (
  value:
    | {
        islandflowDesktopRuntime?: {
          app?: string | null;
          shell?: string | null;
        };
        islandflowDesktop?: DesktopAiBridge;
        navigator?: { userAgent?: string | null };
      }
    | null
    | undefined
): DesktopAiRuntime => {
  const bridge = value?.islandflowDesktop?.ai ? value.islandflowDesktop : null;
  const bridgeAvailable = Boolean(bridge?.ai);
  const shellAvailable =
    bridgeAvailable ||
    detectDesktopRuntimeMarker(value?.islandflowDesktopRuntime) ||
    detectDesktopShell(value?.navigator?.userAgent);

  return {
    shellAvailable,
    bridgeAvailable,
    bridge
  };
};

export const resolveCurrentDesktopAiRuntime = (): DesktopAiRuntime =>
  typeof window === "undefined" ? BROWSER_RUNTIME : resolveDesktopAiRuntime(window);

const isSameDesktopAiRuntime = (
  left: DesktopAiRuntime,
  right: DesktopAiRuntime
): boolean =>
  left.shellAvailable === right.shellAvailable &&
  left.bridgeAvailable === right.bridgeAvailable &&
  left.bridge === right.bridge;

export const createUnavailableState = (runtime?: Partial<DesktopAiRuntime>): IslandflowAiState => {
  const shellAvailable = Boolean(runtime?.shellAvailable || runtime?.bridgeAvailable);
  const bridgeAvailable = Boolean(runtime?.bridgeAvailable);
  const transportError = !shellAvailable
    ? "Desktop AI is only available inside the Islandflow Electron app."
    : bridgeAvailable
    ? "The desktop AI bridge loaded, but its initial state could not be read."
    : "Islandflow Desktop is open, but the native AI bridge is unavailable in this session.";
  const loginMessage = !shellAvailable
    ? "Open Islandflow Desktop to connect a ChatGPT or Codex account."
    : bridgeAvailable
    ? "The desktop bridge connected, but its initial state did not load. Retry the action or restart Islandflow if this persists."
    : "This desktop window is missing its native AI bridge. Reload the window or restart Islandflow if this persists.";

  return {
    desktopAvailable: shellAvailable,
    transportStatus: shellAvailable ? "error" : "stopped",
    transportError,
    profiles: [
      {
        id: "managed-chatgpt",
        label: "Managed ChatGPT login",
        description: shellAvailable
          ? "Managed ChatGPT login belongs to the desktop shell, but this window is not connected to the native bridge yet."
          : "Available only in the desktop app.",
        mode: "managed-chatgpt",
        enabled: shellAvailable,
        selected: true,
        statusLabel: shellAvailable ? "Bridge unavailable" : "Desktop only"
      }
    ],
    selectedProfileId: "managed-chatgpt",
    account: {
      loggedIn: false,
      email: null,
      planType: null,
      authMode: null,
      requiresOpenaiAuth: true,
      login: {
        status: "idle",
        message: loginMessage
      }
    },
    preferences: {
      model: null,
      reasoningEffort: "high"
    },
    models: [],
    rateLimitsByLimitId: {},
    usage: {
      today: {
        breakdown: {
          totalTokens: 0,
          inputTokens: 0,
          cachedInputTokens: 0,
          outputTokens: 0,
          reasoningOutputTokens: 0
        },
        normalizedCostUsd: 0,
        turnCount: 0,
        activeDays: 0
      },
      lifetime: {
        breakdown: {
          totalTokens: 0,
          inputTokens: 0,
          cachedInputTokens: 0,
          outputTokens: 0,
          reasoningOutputTokens: 0
        },
        normalizedCostUsd: 0,
        turnCount: 0,
        activeDays: 0
      },
      recentTurns: []
    },
    tasks: [],
    updatedAt: Date.now()
  };
};

const DesktopAiContext = createContext<DesktopAiContextValue | null>(null);

const rejectDesktopOnly = async (): Promise<never> => {
  throw new Error("Desktop AI is only available inside the Islandflow Electron app.");
};

export function DesktopAiProvider({ children }: { children: ReactNode }) {
  const initialRuntime = resolveCurrentDesktopAiRuntime();
  const [runtime, setRuntime] = useState<DesktopAiRuntime>(initialRuntime);
  const [state, setState] = useState<IslandflowAiState>(() =>
    createUnavailableState(initialRuntime)
  );

  useEffect(() => {
    if (typeof window === "undefined") {
      return;
    }

    let disposed = false;
    let activeBridge: DesktopAiBridge | null = null;
    let unsubscribe = () => {};
    let pollTimer: number | null = null;
    let attempts = 0;
    const clearPendingPoll = () => {
      if (pollTimer !== null) {
        window.clearTimeout(pollTimer);
        pollTimer = null;
      }
    };

    const replaceRuntime = (nextRuntime: DesktopAiRuntime) => {
      setRuntime((currentRuntime) =>
        isSameDesktopAiRuntime(currentRuntime, nextRuntime) ? currentRuntime : nextRuntime
      );
    };

    const attachBridge = (nextBridge: DesktopAiBridge | null, runtimeSnapshot: DesktopAiRuntime) => {
      if (activeBridge === nextBridge) {
        return;
      }

      unsubscribe();
      unsubscribe = () => {};
      activeBridge = nextBridge;
      if (!nextBridge) {
        return;
      }

      const bridgeAtAttach = nextBridge;
      void bridgeAtAttach.ai.getState().then(
        (nextState) => {
          if (!disposed && activeBridge === bridgeAtAttach) {
            setState(nextState);
          }
        },
        () => {
          if (!disposed && activeBridge === bridgeAtAttach) {
            setState(createUnavailableState(runtimeSnapshot));
          }
        }
      );

      unsubscribe = bridgeAtAttach.ai.subscribe((nextState) => {
        if (!disposed && activeBridge === bridgeAtAttach) {
          setState(nextState);
        }
      });
    };

    const syncRuntime = (): boolean => {
      const nextRuntime = resolveDesktopAiRuntime(window);
      replaceRuntime(nextRuntime);
      if (nextRuntime.bridge) {
        attachBridge(nextRuntime.bridge, nextRuntime);
        return true;
      }

      attachBridge(null, nextRuntime);
      setState(createUnavailableState(nextRuntime));
      return false;
    };

    const pollForBridge = () => {
      if (disposed) {
        return;
      }

      attempts += 1;
      if (syncRuntime() || attempts >= BRIDGE_POLL_MAX_ATTEMPTS) {
        clearPendingPoll();
        return;
      }
      pollTimer = window.setTimeout(pollForBridge, BRIDGE_POLL_INTERVAL_MS);
    };

    const startRetryWindow = () => {
      attempts = 0;
      clearPendingPoll();
      pollTimer = window.setTimeout(pollForBridge, BRIDGE_POLL_INTERVAL_MS);
    };

    const onWindowLifecycle = () => {
      if (!syncRuntime()) {
        startRetryWindow();
      }
    };

    window.addEventListener("focus", onWindowLifecycle);
    window.addEventListener("pageshow", onWindowLifecycle);

    if (!syncRuntime()) {
      startRetryWindow();
    }

    return () => {
      disposed = true;
      clearPendingPoll();
      window.removeEventListener("focus", onWindowLifecycle);
      window.removeEventListener("pageshow", onWindowLifecycle);
      unsubscribe();
    };
  }, []);

  const value = useMemo<DesktopAiContextValue>(
    () => ({
      bridgeAvailable: runtime.bridgeAvailable,
      shellAvailable: runtime.shellAvailable,
      state,
      loginWithBrowser: runtime.bridge?.ai.loginWithBrowser ?? rejectDesktopOnly,
      loginWithDeviceCode: runtime.bridge?.ai.loginWithDeviceCode ?? rejectDesktopOnly,
      cancelLogin: runtime.bridge?.ai.cancelLogin ?? rejectDesktopOnly,
      logout: runtime.bridge?.ai.logout ?? rejectDesktopOnly,
      updatePreferences: runtime.bridge?.ai.updatePreferences ?? rejectDesktopOnly,
      runTask: runtime.bridge?.ai.runTask ?? rejectDesktopOnly,
      cancelTask: runtime.bridge?.ai.cancelTask ?? rejectDesktopOnly
    }),
    [runtime, state]
  );

  return <DesktopAiContext.Provider value={value}>{children}</DesktopAiContext.Provider>;
}

export const useDesktopAi = (): DesktopAiContextValue => {
  const value = useContext(DesktopAiContext);
  if (!value) {
    throw new Error("Desktop AI context missing");
  }
  return value;
};
