import { create } from 'zustand';
import { createInitialAgentRunState } from '@/lib/markdown';
import type {
  ActivePaneSession,
  AgentRunState,
  AppSettings,
  DeckEvent,
  ToolsTabId,
  V4RuntimeEvent,
  V4SubAgentState,
  VigthoriaAuthStatus,
  WorkbenchUpdateInfo,
  V4ProviderId,
  WorkspaceDefinition,
  WorkspacePresetId,
} from '@/types/workbench';
import {
  appVersion,
  checkWorkbenchUpdate,
  createWorkspaceFromPreset,
  detectVigthoriaCli,
  downloadWorkbenchUpdate,
  installWorkbenchUpdate,
  launchVigthoriaLogin,
  settingsGet,
  settingsSave,
  vigthoriaAuthStatus,
  vigthoriaAuthToken,
  workspaceList,
  workspaceSave,
} from '@/lib/tauri-bridge';
import { applyDeckEvent } from '@/lib/deck-events';

const AUTH_POLL_MS = 45_000;

const defaultAuth: VigthoriaAuthStatus = {
  cliInstalled: false,
  hasToken: false,
  authenticated: false,
  isAdmin: false,
  isMasterAdmin: false,
  apiUrl: 'https://coder.vigthoria.io',
  modelsApiUrl: 'https://api.vigthoria.io',
  message: 'Checking login…',
};

const defaultSettings: AppSettings = {
  theme: 'vigthoria-dark',
  showReportPanel: true,
  hyperloopUrl: 'https://hyperloop.vigthoria.io',
  templateUrl: 'https://templates.vigthoria.io',
  comfortPromptEnabled: true,
  goaAutoRoute: true,
  toolsTab: 'report',
};

function parseToolsTab(value: string | undefined): ToolsTabId {
  if (value === 'hyperloop' || value === 'templates' || value === 'report' || value === 'goa') {
    return value;
  }
  return 'report';
}

function toAgentStatus(event: string): V4SubAgentState['status'] | null {
  switch (event) {
    case 'sub_agent_spawn':
      return 'spawned';
    case 'sub_agent_output':
    case 'sandbox_stdout':
    case 'sandbox_stderr':
      return 'running';
    case 'sub_agent_finish':
      return 'finished';
    case 'sub_agent_error':
      return 'error';
    case 'sub_agent_cancelled':
      return 'cancelled';
    case 'sub_agent_timeout':
      return 'timeout';
    default:
      return null;
  }
}

function compactText(value: unknown): string {
  if (typeof value === 'string') return value.slice(0, 260);
  if (value == null) return '';
  return String(value).slice(0, 260);
}

interface WorkbenchState {
  workspaces: WorkspaceDefinition[];
  activeWorkspaceId: string | null;
  activeSessions: ActivePaneSession[];
  agentRun: AgentRunState;
  settings: AppSettings;
  vigthoriaCliPath: string | null;
  settingsOpen: boolean;
  loading: boolean;
  bootstrapped: boolean;
  terminalEpoch: number;
  auth: VigthoriaAuthStatus;
  authChecking: boolean;
  authAutoLoginTriggered: boolean;
  mainAgentPtyId: string | null;
  promptHistory: string[];
  toolsTab: ToolsTabId;
  serviceResult: string | null;
  v4SubAgents: V4SubAgentState[];
  appVersion: string | null;
  updateInfo: WorkbenchUpdateInfo | null;
  updateChecking: boolean;
  updateBusy: boolean;
  updateMessage: string | null;
  bootstrap: () => Promise<void>;
  refreshAuth: (verify?: boolean) => Promise<void>;
  checkAppUpdate: () => Promise<void>;
  installAppUpdate: () => Promise<void>;
  startLogin: () => Promise<string>;
  getAuthToken: () => Promise<string | null>;
  selectWorkspace: (id: string) => Promise<void>;
  addWorkspace: (name: string, rootPath: string, presetId: WorkspacePresetId, options?: { v4Provider?: V4ProviderId }) => Promise<void>;
  setWorkspaceRootPath: (rootPath: string) => Promise<boolean>;
  saveActiveWorkspaceSettings: (patch: Partial<WorkspaceDefinition['vigthoria']>) => Promise<void>;
  setActiveSessions: (sessions: ActivePaneSession[]) => void;
  updateSessionActivity: (ptyId: string, active: boolean) => void;
  handleDeckEvent: (event: DeckEvent) => void;
  resetAgentRun: () => void;
  setSettingsOpen: (open: boolean) => void;
  updateSettings: (patch: Partial<AppSettings>) => Promise<void>;
  setMainAgentPtyId: (ptyId: string | null) => void;
  pushPromptHistory: (text: string) => void;
  setToolsTab: (tab: ToolsTabId) => Promise<void>;
  setServiceResult: (result: string | null) => void;
  handleV4RuntimeEvent: (event: V4RuntimeEvent) => void;
}

export const useWorkbenchStore = create<WorkbenchState>((set, get) => ({
  workspaces: [],
  activeWorkspaceId: null,
  activeSessions: [],
  agentRun: createInitialAgentRunState(),
  settings: defaultSettings,
  vigthoriaCliPath: null,
  settingsOpen: false,
  loading: true,
  bootstrapped: false,
  terminalEpoch: 0,
  auth: defaultAuth,
  authChecking: false,
  authAutoLoginTriggered: false,
  mainAgentPtyId: null,
  promptHistory: [],
  toolsTab: 'report',
  serviceResult: null,
  v4SubAgents: [],
  appVersion: null,
  updateInfo: null,
  updateChecking: false,
  updateBusy: false,
  updateMessage: null,

  bootstrap: async () => {
    if (get().bootstrapped) return;
    set({ loading: true });
    try {
      const [workspaces, cliPath, settings, version] = await Promise.all([
        workspaceList(),
        detectVigthoriaCli(),
        settingsGet().catch(() => defaultSettings),
        appVersion().catch(() => null),
      ]);
      const merged = {
        ...defaultSettings,
        ...settings,
        comfortPromptEnabled: settings?.comfortPromptEnabled ?? true,
      };
      const toolsTab = parseToolsTab(merged.toolsTab);
      const activeWorkspaceId = merged.lastWorkspaceId
        && workspaces.some((w) => w.id === merged.lastWorkspaceId)
        ? merged.lastWorkspaceId
        : workspaces[0]?.id ?? null;
      set({
        workspaces,
        activeWorkspaceId,
        vigthoriaCliPath: cliPath,
        settings: merged,
        toolsTab,
        appVersion: version,
        loading: false,
        bootstrapped: true,
      });
      await get().refreshAuth(true);
      void get().checkAppUpdate();
    } catch {
      set({ loading: false, bootstrapped: true });
    }
  },

  refreshAuth: async (verify = true) => {
    set({ authChecking: true });
    try {
      const status = await vigthoriaAuthStatus(verify);
      set({ auth: status, authChecking: false });
      if (status.authenticated) {
        // A working session clears the one-shot auto-login guard so a
        // *future* expiry can trigger auto-login again.
        if (get().authAutoLoginTriggered) set({ authAutoLoginTriggered: false });
      } else if (status.hasToken && !get().authAutoLoginTriggered) {
        // We have a token on disk but the server just rejected it (session
        // expired / revoked). Don't just show a small banner and wait for the
        // user to notice -- proactively relaunch the login flow once, and
        // surface Settings so the outcome is visible.
        set({ authAutoLoginTriggered: true, settingsOpen: true });
        void get().startLogin().catch(() => {
          // If auto-launch fails (e.g. CLI missing), leave it to the manual
          // Login button in the auth gate / Settings modal.
        });
      }
    } catch {
      set({
        authChecking: false,
        auth: {
          ...defaultAuth,
          message: 'Could not read CLI auth status.',
        },
      });
    }
  },

  startLogin: async () => {
    const message = await launchVigthoriaLogin(get().settings.vigthoriaCliPath);
    window.setTimeout(() => {
      void get().refreshAuth(true);
    }, 1500);
    return message;
  },

  getAuthToken: async () => {
    try {
      return await vigthoriaAuthToken();
    } catch {
      return null;
    }
  },

  selectWorkspace: async (id) => {
    set({
      activeWorkspaceId: id,
      activeSessions: [],
      agentRun: createInitialAgentRunState(),
      mainAgentPtyId: null,
      terminalEpoch: get().terminalEpoch + 1,
      v4SubAgents: [],
    });
    const settings = { ...get().settings, lastWorkspaceId: id };
    set({ settings });
    try {
      await settingsSave(settings);
    } catch {
      // local-only fallback in dev web mode
    }
  },

  addWorkspace: async (name, rootPath, presetId, options) => {
    const workspace = createWorkspaceFromPreset(presetId, name, rootPath, options);
    const saved = await workspaceSave(workspace);
    set((state) => ({
      workspaces: [...state.workspaces.filter((w) => w.id !== saved.id), saved],
      activeWorkspaceId: saved.id,
      activeSessions: [],
      agentRun: createInitialAgentRunState(),
      mainAgentPtyId: null,
      v4SubAgents: [],
    }));
    await get().selectWorkspace(saved.id);
  },

  setWorkspaceRootPath: async (rootPath) => {
    const activeId = get().activeWorkspaceId;
    if (!activeId) return false;
    const current = get().workspaces.find((w) => w.id === activeId);
    if (!current) return false;
    const trimmed = rootPath.trim();
    const updated: WorkspaceDefinition = {
      ...current,
      rootPath: trimmed,
      layout: {
        ...current.layout,
        panes: current.layout.panes.map((pane) => ({ ...pane, cwd: trimmed })),
      },
      updatedAt: String(Date.now()),
    };
    const saved = await workspaceSave(updated);
    set((state) => ({
      workspaces: state.workspaces.map((w) => (w.id === saved.id ? saved : w)),
      activeSessions: [],
      mainAgentPtyId: null,
      terminalEpoch: state.terminalEpoch + 1,
      v4SubAgents: [],
    }));
    return true;
  },

  saveActiveWorkspaceSettings: async (patch) => {
    const activeId = get().activeWorkspaceId;
    if (!activeId) return;
    const current = get().workspaces.find((w) => w.id === activeId);
    if (!current) return;
    const updated: WorkspaceDefinition = {
      ...current,
      vigthoria: { ...current.vigthoria, ...patch },
      updatedAt: String(Date.now()),
    };
    const saved = await workspaceSave(updated);
    set((state) => ({
      workspaces: state.workspaces.map((w) => (w.id === saved.id ? saved : w)),
    }));
  },

  setActiveSessions: (sessions) => set({ activeSessions: sessions }),

  updateSessionActivity: (ptyId, active) =>
    set((state) => ({
      activeSessions: state.activeSessions.map((session) =>
        session.ptyId === ptyId ? { ...session, active } : session,
      ),
    })),

  handleDeckEvent: (event) =>
    set((state) => ({
      agentRun: applyDeckEvent(state.agentRun, event),
    })),

  resetAgentRun: () => set({ agentRun: createInitialAgentRunState() }),

  setSettingsOpen: (open) => {
    set({ settingsOpen: open });
    if (open) {
      // Re-verify auth against the live backend every time Settings is opened,
      // instead of showing whatever status was cached at app bootstrap. Without
      // this, a token that expires/becomes invalid mid-session keeps showing
      // "Logged in" here even after the CLI itself starts rejecting it.
      void get().refreshAuth(true);
    }
  },

  updateSettings: async (patch) => {
    const next = { ...get().settings, ...patch };
    if (patch.toolsTab) {
      set({ toolsTab: parseToolsTab(patch.toolsTab) });
    }
    set({ settings: next });
    try {
      await settingsSave(next);
    } catch {
      // ignore in browser-only dev
    }
  },

  setMainAgentPtyId: (ptyId) => set({ mainAgentPtyId: ptyId }),

  pushPromptHistory: (text) =>
    set((state) => {
      const trimmed = text.trim();
      if (!trimmed) return state;
      const next = [trimmed, ...state.promptHistory.filter((h) => h !== trimmed)].slice(0, 12);
      return { promptHistory: next };
    }),

  setToolsTab: async (tab) => {
    set({ toolsTab: tab, serviceResult: null });
    await get().updateSettings({ toolsTab: tab });
  },

  setServiceResult: (result) => set({ serviceResult: result }),

  checkAppUpdate: async () => {
    set({ updateChecking: true, updateMessage: null });
    try {
      const info = await checkWorkbenchUpdate();
      set({ updateInfo: info, updateChecking: false });
    } catch (err) {
      set({
        updateChecking: false,
        updateMessage: err instanceof Error ? err.message : String(err),
      });
    }
  },

  installAppUpdate: async () => {
    const info = get().updateInfo;
    if (!info || !info.updateAvailable || !info.downloadUrl || !info.sha256) return;
    set({ updateBusy: true, updateMessage: 'Downloading update…' });
    try {
      const path = await downloadWorkbenchUpdate(info.downloadUrl, info.sha256);
      set({ updateMessage: 'Launching installer…' });
      await installWorkbenchUpdate(path);
    } catch (err) {
      set({
        updateBusy: false,
        updateMessage: err instanceof Error ? err.message : String(err),
      });
    }
  },

  handleV4RuntimeEvent: (event) =>
    set((state) => {
      const status = toAgentStatus(event.event);
      if (!status) return state;
      const fallbackId = `v4-${state.v4SubAgents.length + 1}`;
      const agentId = event.agent_id?.trim() || fallbackId;
      const payload = event.payload ?? {};
      const output = compactText((payload as Record<string, unknown>).output ?? (payload as Record<string, unknown>).content ?? event.task);
      const now = typeof event.ts === 'number' ? event.ts * 1000 : Date.now();
      const existing = state.v4SubAgents.find((agent) => agent.agentId === agentId);
      const nextLog = output
        ? [...(existing?.outputLog ?? []), output].slice(-120)
        : (existing?.outputLog ?? []);
      const nextAgent: V4SubAgentState = existing
        ? {
          ...existing,
          status,
          task: event.task?.trim() || existing.task,
          lastOutput: output || existing.lastOutput,
          outputLog: nextLog,
          updatedAt: now,
        }
        : {
          agentId,
          task: event.task?.trim() || 'Sub-agent task',
          status,
          lastOutput: output || undefined,
          outputLog: output ? [output] : [],
          updatedAt: now,
        };

      const withoutCurrent = state.v4SubAgents.filter((agent) => agent.agentId !== agentId);
      const next = [nextAgent, ...withoutCurrent].slice(0, 12);
      return { v4SubAgents: next };
    }),
}));

export function useActiveWorkspace(): WorkspaceDefinition | null {
  const workspaces = useWorkbenchStore((s) => s.workspaces);
  const activeWorkspaceId = useWorkbenchStore((s) => s.activeWorkspaceId);
  return workspaces.find((w) => w.id === activeWorkspaceId) ?? null;
}

let authPollStarted = false;

export function startAuthPolling(): void {
  if (authPollStarted) return;
  authPollStarted = true;
  window.setInterval(() => {
    if (useWorkbenchStore.getState().bootstrapped) {
      void useWorkbenchStore.getState().refreshAuth(true);
    }
  }, AUTH_POLL_MS);
}
