import React, { useEffect, useMemo, useRef, useState } from "react";
import { createRoot } from "react-dom/client";
import { useAppController } from "./app/useAppController.js";
import { BackNavigationProvider } from "./app/BackNavigationProvider.jsx";
import { I18nProvider } from "./shared/I18nProvider.jsx";
import { useI18n } from "./shared/I18nProvider.jsx";
import { ThemeProvider } from "./shared/ThemeProvider.jsx";
import { useBackLayer } from "./app/useBackLayer.js";
import { StartupScreen } from "./app/StartupScreen.jsx";
import { useExitTransition } from "./app/useExitTransition.js";
import { useAdaptiveSplitView } from "./app/useAdaptiveSplitView.js";
import { request } from "./app/api.js";
import { useAccounts } from "./features/accounts/useAccounts.js";
import { useProEntitlement } from "./features/pro/useProEntitlement.js";
import { ProPurchaseSheet } from "./features/pro/ProPurchaseSheet.jsx";
import { runProSheetAction } from "./features/pro/pro-sheet-action.js";
import { readAccountMosaicPreference, readConversationAccountVisibilityPreference, writeAccountMosaicPreference, writeConversationAccountVisibilityPreference } from "./features/accounts/account-mosaic-preference.js";
import { ChangeDiffSheet } from "./features/changes/ChangeDiffSheet.jsx";
import { useChanges } from "./features/changes/useChanges.js";
import { ChatsPanel } from "./features/chats/ChatsPanel.jsx";
import { NewChatPanel } from "./features/chats/NewChatPanel.jsx";
import { registerBrowsedWorkspace, registerProjectWorkspace } from "./features/chats/project-api.js";
import { recentProjectFolders } from "./features/chats/project-picker.js";
import { useChats } from "./features/chats/useChats.js";
import { ConversationPanel } from "./features/conversation/ConversationPanel.jsx";
import { conversationDisplayTitle } from "./features/conversation/conversation-title.js";
import { useConversation } from "./features/conversation/useConversation.js";
import { ContextBar } from "./features/context/ContextBar.jsx";
import { SettingsWindow } from "./features/context/SettingsWindow.jsx";
import { accountSwitchLockAccountId, accountSwitchLocked, headerProject } from "./features/context/context-state.js";
import { actionBlockedReason, projectSetupPrompt } from "./features/setup/setup-errors.js";
import { ConversationAccountBar, StatusPanel } from "./features/status/StatusPanel.jsx";
import { useStatus } from "./features/status/useStatus.js";
import { statusOwnerId, visibleTokenUsage } from "./features/status/status-state.js";
import { useNotifications } from "./features/notifications/useNotifications.js";
import { notificationClickAction } from "./features/notifications/notification-types.js";
import { NativeConnectionGate } from "./features/connection/NativeConnectionGate.jsx";
import { AuthenticationGate } from "./features/auth/AuthenticationGate.jsx";
import { connectionStatusKey } from "./features/connection/connection-status.js";
import { useConnectionRecovery } from "./features/connection/useConnectionRecovery.js";
import { platformRuntime } from "./platform/runtime.js";
import { projectConversation } from "./conversation.js";
import { ReviewDemoProvider } from "./features/review-demo/ReviewDemo.jsx";
import "./shared/styles/base.css";
import "./shared/pull-to-refresh.css";
import "./app/app.css";
import "./features/accounts/accounts.css";
import "./features/context/context.css";
import "./features/chats/chats.css";
import "./features/chats/project-picker.css";
import "./features/conversation/conversation.css";
import "./features/conversation/composer-menus.css";
import "./features/changes/changes.css";
import "./features/changes/change-sheet.css";
import "./features/status/status.css";
import "./features/notifications/notifications.css";
import "./features/notifications/notification-settings.css";
import "./features/connection/connection.css";
import "./features/pro/pro.css";
import "./shared/styles/action-dialog.css";
import "./shared/styles/feedback.css";

if (!platformRuntime.isNative && "serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker.register("/sw.js").catch(() => {});
  });
}

export function App() {
  const { t } = useI18n();
  const [accountMosaic, setAccountMosaicState] = useState(() => readAccountMosaicPreference());
  const [conversationAccountVisible, setConversationAccountVisibleState] = useState(() => readConversationAccountVisibilityPreference());
  const connectivity = useConnectionRecovery();
  const app = useAppController();
  const pro = useProEntitlement();
  const [proOpen, setProOpen] = useState(false);
  const [proProduct, setProProduct] = useState({ productId: "pro_multi_account", localizedPrice: null, available: false });
  const adaptiveSplitView = useAdaptiveSplitView();
  const retryTransitionRef = useRef(() => {});
  const appWasReadyRef = useRef(false);
  async function openPro() { setProOpen(true); setProProduct(await pro.getProduct()); }
  const accounts = useAccounts({ replaceAccounts: app.replaceAccounts, accounts: app.accounts, maxAccounts: pro.maxAccounts, onProRequired: openPro });
  function finishPro(action) { return runProSheetAction(action, { close: () => setProOpen(false), resume: () => accounts.add({ bypassPro: true }) }); }
  const status = useStatus(statusOwnerId(app.session, app.accountId));
  const changes = useChanges(app.session);
  const conversation = useConversation({
    session: app.session,
    accountId: app.accountId,
    accounts: app.accounts,
    workspaceId: app.workspaceId,
    recoveryVersion: connectivity.recoveryVersion,
    onSessionUpdated: app.updateSession,
    onFilesChanged: changes.acceptFiles,
    onFileActivity: changes.refresh
  });
  const displaySession = conversation.displaySession ?? app.session;
  const transitionSession = app.chatTransition ? {
    id: `opening-${app.chatTransition.requestId}`,
    accountId: app.chatTransition.target?.accountId || app.accountId,
    selectedExecutionAccountId: app.accountId,
    executionAccounts: app.accounts,
    workspaceId: app.chatTransition.target?.workspaceId || "",
    projectLabel: app.chatTransition.target?.projectLabel || "",
    projectless: Boolean(app.chatTransition.target?.projectless),
    codexThreadId: app.chatTransition.target?.codexThreadId || "",
    status: "opening",
    queuedMessages: []
  } : null;
  const visibleSession = transitionSession ?? displaySession;
  const transitionConversation = app.chatTransition?.target?.optimisticEvents
    ? projectConversation(app.chatTransition.target.optimisticEvents ?? [])
    : [];
  const visibleConversation = app.chatTransition ? transitionConversation : conversation.conversation;
  const visibleConversationTitle = conversationDisplayTitle({
    session: visibleSession,
    transition: app.chatTransition,
    turns: visibleConversation,
    fallback: t("chats.new")
  });
  const notificationCurrentSession = app.tab === "chat" && !app.newChatOpen && !app.chatTransition ? displaySession : null;
  const notifications = useNotifications({ accounts: app.accounts, currentSession: notificationCurrentSession, recoveryVersion: connectivity.recoveryVersion });
  const project = headerProject(visibleSession, app.selectedWorkspace, app.workspaces, app.newChatOpen || app.tab === "threads");
  const accountSwitchAccountId = accountSwitchLockAccountId(notifications.sessions, visibleSession);
  const newChatMotion = useExitTransition(app.closeNewChat);
  const closeNewChat = useBackLayer(app.newChatOpen, newChatMotion.startExit, "new-chat");
  const newChatBlockedReason = actionBlockedReason({
    hasAccount: Boolean(app.accountId),
    hasWorkspace: app.workspaces.length > 0
  });
  const projectSetupMessage = projectSetupPrompt({
    hasAccount: Boolean(app.accountId),
    hasWorkspace: app.workspaces.length > 0
  });

  function handleSessionOpened(result, requestId, initialOptions) {
    if (!app.isCurrentChatTransition(requestId)) return;
    app.openSession(result.session, requestId);
    conversation.replace(result.session.id, result.events ?? [], initialOptions);
  }

  function startChatTransition(label, retry, target) {
    retryTransitionRef.current = retry;
    return app.startChatTransition(label, target);
  }

  function handleAccountChange(accountId) {
    if (app.chatTransition) app.cancelChatTransition();
    app.selectAccount(accountId);
  }

  function closePopupBefore(afterClose) {
    afterClose?.();
  }

  function openSettings() {
    app.openSettings();
  }

  function setAccountMosaic(enabled) {
    const next = Boolean(enabled);
    setAccountMosaicState(next);
    writeAccountMosaicPreference(undefined, next);
  }

  function setConversationAccountVisible(visible) {
    const next = Boolean(visible);
    setConversationAccountVisibleState(next);
    writeConversationAccountVisibilityPreference(undefined, next);
  }

  const settingsMotion = useExitTransition(app.closeSettings);
  const closeSettings = useBackLayer(app.tab === "settings", settingsMotion.startExit, "settings");

  function openArchivedChats() {
    closeSettings(() => {
      chats.setArchived(true);
      app.selectTab("threads");
    });
  }

  async function handleNotificationOpen(notification) {
    if (notificationClickAction(notification.type) !== "open-session") {
      notifications.dismiss(notification.id);
      return;
    }
    if (!notification.sessionId) return;
    notifications.clearOpenError();
    try {
      const result = await request(`/sessions/${encodeURIComponent(notification.sessionId)}`);
      app.openSession(result.session, undefined, { reveal: true });
      notifications.dismissSession(notification.sessionId);
    } catch (error) {
      notifications.failOpen(notification.id);
    }
  }

  async function handleChatOpen(thread) {
    const session = notifications.sessions.find((item) => item.accountId === thread.ownerId && item.codexThreadId === thread.id);
    if (session) notifications.dismissSession(session.id);
    if (thread.workspaceId) app.selectWorkspace(thread.workspaceId);
    await chats.open(thread);
  }

  async function handleActiveSessionOpen(session) {
    notifications.clearOpenError();
    try {
      const result = await request(`/sessions/${encodeURIComponent(session.id)}`);
      app.openSession(result.session, undefined, { reveal: true });
      notifications.dismissSession(session.id);
    } catch (error) {
      notifications.failOpen(session.id);
    }
  }

  async function handleChatArchive(thread) {
    const archived = await chats.archive(thread);
    if (archived && app.session?.accountId === thread.ownerId && app.session?.codexThreadId === thread.id) {
      app.selectTab("threads");
    }
  }

  async function handleChatDelete(thread) {
    const deleted = await chats.deleteArchived(thread);
    if (deleted && app.session?.accountId === thread.ownerId && app.session?.codexThreadId === thread.id) {
      app.selectTab("threads");
    }
  }

  const chats = useChats({
    accountId: app.accountId,
    workspaceId: app.workspaceId,
    recoveryVersion: connectivity.recoveryVersion,
    onSessionOpened: handleSessionOpened,
    onTransitionStarted: startChatTransition,
    onTransitionFailed: app.failChatTransition
  });
  const recentProjects = useMemo(
    () => recentProjectFolders(chats.recentProjectThreads, app.workspaces, chats.hiddenProjects),
    [app.workspaces, chats.hiddenProjects, chats.recentProjectThreads]
  );
  useEffect(() => {
    if (!connectivity.recoveryVersion) return;
    void Promise.allSettled([
      chats.reload({ silent: true }),
      notifications.reload(),
      status.reload({ silent: true })
    ]);
  }, [connectivity.recoveryVersion]);
  const connectionMessageKey = connectionStatusKey({
    connectivityState: connectivity.state,
    appError: app.error,
    usageError: status.error
  });
  const lastConversationWorkspaceId = app.workspaceId || visibleSession?.workspaceId || chats.threads.find((thread) => thread.workspaceId)?.workspaceId;
  const lastConversationWorkspace = app.workspaces.find((workspace) => workspace.id === lastConversationWorkspaceId) ?? app.selectedWorkspace ?? app.workspaces[0];
  const screenLayerActive = (app.tab !== "threads" && app.tab !== "settings")
    || (app.tab === "settings" && app.settingsReturnTab !== "threads");
  const screenMotion = useExitTransition(() => {
    if (app.chatTransition) app.cancelChatTransition();
    else {
      if (app.tab === "chat") chats.reload();
      app.selectTab("threads");
    }
  });
  const closeScreen = useBackLayer(screenLayerActive, screenMotion.startExit, "screen", { preferTop: true });
  function handleProjectChatCreate(workspaceId, message) {
    app.selectWorkspace(workspaceId);
    return message ? chats.createWithMessage(workspaceId, message) : chats.create(workspaceId);
  }
  async function handleSelectProject({ name, create }) {
    const result = await registerProjectWorkspace(name, create);
    app.replaceWorkspaces(result.workspaces, result.workspace.id);
    return result.workspace.id;
  }
  async function handleRegisterProject(name) {
    const result = await registerProjectWorkspace(name, false);
    app.replaceWorkspaces(result.workspaces, result.workspace.id);
    return result.workspace.id;
  }
  async function handleSelectFolder(folderId) {
    const result = await registerBrowsedWorkspace(folderId);
    app.replaceWorkspaces(result.workspaces, result.workspace.id);
    return result.workspace.id;
  }
  const suppressInitialScreenMotion = !appWasReadyRef.current;
  useEffect(() => {
    if (app.bootStatus === "ready") appWasReadyRef.current = true;
  }, [app.bootStatus]);
  if (app.bootStatus === "loading") return <StartupScreen />;
  const chatLayerVisible = app.tab === "chat" || (app.tab === "settings" && app.settingsReturnTab === "chat");
  const splitLayoutActive = adaptiveSplitView && app.tab === "chat" && !app.newChatOpen;
  const screenEnterClass = suppressInitialScreenMotion ? "motion-screen--settled" : "motion-screen--enter";
  return <main className={`app-shell app-shell--ready${splitLayoutActive ? " app-shell--split" : ""}`}>
    <ContextBar account={app.selectedAccount} project={project} session={visibleSession} accounts={app.accounts} accountId={app.accountId} accountMosaic={accountMosaic} accountSwitchLocked={accountSwitchLocked(notifications.sessions, visibleSession)} accountSwitchAccountId={accountSwitchAccountId} notifications={notifications.notifications} sessions={notifications.sessions} currentSession={notificationCurrentSession} threads={chats.threads} notificationError={notifications.openError} notificationRefreshErrors={notifications.refreshErrors} usageStatus={status} onAccountChange={handleAccountChange} onChatOpen={() => app.session && app.selectTab("chat")} onSettingsOpen={openSettings} onPopupOpen={closePopupBefore} manager={accounts} onNotificationOpen={handleNotificationOpen} onNotificationDismiss={notifications.dismiss} onActiveSessionOpen={handleActiveSessionOpen} onNotificationsClear={notifications.clear} onUseResetCredit={status.useResetCredit} />
    <StatusPanel status={status} tokenUsage={visibleTokenUsage(visibleSession, app.accountId)} connectionError={connectionMessageKey ? t(connectionMessageKey) : ""} />
    <ChatsPanel active={!app.newChatOpen && (app.tab === "threads" || splitLayoutActive)} threads={chats.threads} projectThreads={chats.projectThreads} hiddenProjects={chats.hiddenProjects} workspaces={app.workspaces} loading={chats.loading} error={chats.error || chats.projectError} nextCursor={chats.nextCursor} searchQuery={chats.searchQuery} onSearchQuery={chats.setSearchQuery} archived={chats.archived} onArchivedChange={chats.setArchived} activeThreadId={app.session?.codexThreadId} activeOwnerId={app.session?.accountId} sessions={notifications.sessions} currentSession={displaySession} notifications={notifications.notifications} canCreate={!newChatBlockedReason} createBlockedReason={newChatBlockedReason} setupPrompt={projectSetupMessage} onOpen={handleChatOpen} onArchive={handleChatArchive} onDelete={handleChatDelete} onRename={chats.rename} onPin={chats.pin} onHideProject={chats.hideProject} onLoadMore={chats.loadMore} onRetry={chats.reload} onRefresh={chats.reload} onNewChat={app.startNewChat} onProjectChange={app.selectWorkspace} onCreateProjectChat={handleProjectChatCreate} onCreateEmptyProjectChat={handleProjectChatCreate} />
    {chatLayerVisible && <div className={`motion-screen${conversationAccountVisible ? " chat-account-offset" : ""} ${screenEnterClass}${screenMotion.exiting ? " motion-screen--exit" : ""}`} aria-hidden={app.tab !== "chat"} inert={app.tab !== "chat"}>{conversationAccountVisible && <ConversationAccountBar session={visibleSession} accounts={app.accounts} accountMosaic={accountMosaic} />}<ConversationPanel session={visibleSession} title={visibleConversationTitle} conversation={visibleConversation} loading={app.chatTransition ? false : conversation.loading} transition={app.chatTransition} changedFiles={app.chatTransition ? [] : changes.files} now={conversation.now} message={app.chatTransition ? "" : conversation.message} capabilities={conversation.capabilities} composerOptions={conversation.composerOptions} attachments={app.chatTransition ? [] : conversation.attachments} selectedSkills={app.chatTransition ? [] : conversation.selectedSkills} workspaces={app.workspaces} workspaceId={app.workspaceId} submitPending={conversation.submitPending} stopPending={conversation.stopPending} queuedActionPending={conversation.queuedActionPending} approvalDecisionPending={conversation.approvalDecisionPending} onBack={closeScreen} onTransitionRetry={() => retryTransitionRef.current()} onMessageChange={conversation.setMessage} onSend={conversation.send} onStop={conversation.stop} onSteerQueued={conversation.steerQueued} onDeleteQueued={conversation.deleteQueued} onDecision={conversation.decide} onOpenChangedFile={changes.open} onAttach={conversation.attach} onRegisterProject={handleRegisterProject} onWorkspaceChange={app.selectWorkspace} onUploadAttachment={conversation.uploadAttachment} onRemoveAttachment={conversation.removeAttachment} onComposerOption={conversation.setComposerOption} onToggleSkill={conversation.toggleSkill} /></div>}
    {app.tab === "settings" && <div className={`motion-screen ${screenEnterClass}${settingsMotion.exiting ? " motion-screen--exit" : ""}`}><SettingsWindow accounts={app.accounts} accountMosaic={accountMosaic} manager={accounts} pro={pro} conversationAccountVisible={conversationAccountVisible} onAccountMosaicChange={setAccountMosaic} onConversationAccountVisibleChange={setConversationAccountVisible} onBack={closeSettings} onArchivedOpen={openArchivedChats} /></div>}
    <ProPurchaseSheet open={proOpen} product={proProduct} entitlement={pro} outcome={pro.outcome} busy={pro.busy} onClose={() => setProOpen(false)} onPurchase={() => finishPro(pro.purchase)} onRestore={() => finishPro(pro.restore)} />
    {app.newChatOpen && app.tab !== "settings" && <div className={`motion-screen ${screenEnterClass}${newChatMotion.exiting ? " motion-screen--exit" : ""}`}><NewChatPanel selectedWorkspace={lastConversationWorkspace} recentProjects={recentProjects} capabilities={conversation.capabilities} selectedSkills={conversation.selectedSkills} onBack={closeNewChat} onWorkspaceChange={app.selectWorkspace} onSelectProject={handleSelectProject} onSelectFolder={handleSelectFolder} onCreateWithMessage={chats.createWithMessage} onCreateWithoutProject={(composerOptions) => chats.create("", composerOptions)} onToggleSkill={conversation.toggleSkill} /></div>}
    <ChangeDiffSheet open={changes.sheetOpen} files={changes.sheetFiles} diffs={changes.selectedDiffs} pending={changes.pending} error={changes.error} onClose={changes.closeSheet} />
  </main>;
}

createRoot(document.getElementById("root")).render(<ThemeProvider><I18nProvider><BackNavigationProvider><ReviewDemoProvider><NativeConnectionGate><AuthenticationGate><App /></AuthenticationGate></NativeConnectionGate></ReviewDemoProvider></BackNavigationProvider></I18nProvider></ThemeProvider>);
