import type { ConversationState, WorkerContinuityViewFrame } from '@platform-modules/chatgpt-orchestrator-protocol';
import type { ManagedBinding, RuntimeStatus } from './storage.js';

export const UI_MESSAGE_KIND = 'chatgpt-orchestrator.ui' as const;
export const OVERLAY_MESSAGE_KIND = 'chatgpt-orchestrator.overlay' as const;

export type UiAction = 'pause' | 'continue-now' | 'continue-anyway' | 'stop' | 'set-auto-resume';
export type StallState = 'none' | 'warning' | 'paused' | 'unavailable';
export type AutoResumeSummary = 'on' | 'off' | 'mixed' | 'unavailable';

export type BackendContinuityView = WorkerContinuityViewFrame;

export interface ContinuityWorkerView {
  workerId: string;
  label: string;
  hierarchicalName: string | null;
  assignmentTitle: string | null;
  runId: string | null;
  runTitle: string | null;
  conversationState: ConversationState;
  conversationUrl: string;
  statusLabel: string;
  tabId: number;
  currentTab: boolean;
  policyMode: 'auto' | 'manual' | null;
  policySource: 'worker' | 'run' | 'default' | null;
  runPolicyMode: 'auto' | 'manual' | null;
  workerPolicyOverride: boolean;
  deadmanFallbackEnabled: boolean;
  deadmanThresholdMs: number | null;
  attemptNumber: number | null;
  autoResumeCount: number | null;
  lastProgressAt: string | null;
  blockReason: string | null;
  pauseReason: 'user' | 'stall' | 'error' | null;
  noProgressAttempts: number | null;
  stallWarningAt: string | null;
  stallPausedAt: string | null;
  stallState: StallState;
  recentEvents: BackendContinuityView['recentEvents'] | null;
  needsUser: boolean;
  controlAvailable: boolean;
}

export interface ContinuityRunView {
  runId: string;
  title: string;
  policyMode: 'auto' | 'manual';
  workerCount: number;
  unfinishedCount: number;
  controlsAvailable: boolean;
}

export interface ContinuityUiCapabilities {
  workerControls: boolean;
  runControls: boolean;
  runMetadata: boolean;
  policySource: boolean;
  stallMetadata: boolean;
  recentEvents: boolean;
}

export interface ContinuityUiSnapshot {
  connection: RuntimeStatus | null;
  overlayVisible: boolean;
  workers: ContinuityWorkerView[];
  runs: ContinuityRunView[];
  currentWorker: ContinuityWorkerView | null;
  counts: { active: number; working: number; resuming: number; needsUser: number };
  autoResume: AutoResumeSummary;
  capabilities: ContinuityUiCapabilities;
}

export interface UiSnapshotRequest { kind: typeof UI_MESSAGE_KIND; action: 'snapshot'; tabId?: number }
export interface UiWorkerActionRequest { kind: typeof UI_MESSAGE_KIND; action: 'pause' | 'continue-now' | 'continue-anyway' | 'stop'; workerId: string }
export interface UiPolicyActionRequest { kind: typeof UI_MESSAGE_KIND; action: 'set-auto-resume'; workerId: string; enabled: boolean }
export interface UiRunActionRequest { kind: typeof UI_MESSAGE_KIND; action: 'pause-run' | 'resume-run' | 'stop-run'; runId: string }
export interface UiRunPolicyActionRequest { kind: typeof UI_MESSAGE_KIND; action: 'set-run-auto-resume'; runId: string; enabled: boolean }
export type UiRequest = UiSnapshotRequest | UiWorkerActionRequest | UiPolicyActionRequest | UiRunActionRequest | UiRunPolicyActionRequest;

export interface OverlayUpdate {
  kind: typeof OVERLAY_MESSAGE_KIND;
  worker: ContinuityWorkerView | null;
  overlayVisible: boolean;
}

function conversationStatusLabel(state: ConversationState): string {
  switch (state) {
    case 'generating': return 'Working';
    case 'loading': return 'Starting';
    case 'ready': return 'Ready';
    case 'idle': return 'Idle';
    case 'error': return 'Execution issue';
    case 'closed': return 'Closed';
  }
}

function continuityStatusLabel(view: BackendContinuityView | undefined, state: ConversationState): string {
  if (!view) return conversationStatusLabel(state);
  if (view.needsUser || view.disposition === 'awaiting_human') return 'Needs you';
  if (view.isResuming || view.disposition === 'awaiting_execution') return 'Resuming';
  if (view.isWorking) return 'Working';
  if (view.disposition === 'awaiting_dependency') return 'Waiting';
  if (view.disposition === 'paused') return 'Paused';
  if (view.disposition === 'terminal') return 'Complete';
  return conversationStatusLabel(state);
}

function stallState(view: BackendContinuityView | undefined): StallState {
  if (!view) return 'unavailable';
  if (view.pauseReason === 'stall' || view.stallPausedAt) return 'paused';
  if (view.stallWarningAt) return 'warning';
  return 'none';
}

function autoResumeSummary(workers: ContinuityWorkerView[]): AutoResumeSummary {
  const modes = workers.map((worker) => worker.policyMode).filter((mode): mode is 'auto' | 'manual' => mode !== null);
  if (modes.length === 0) return 'unavailable';
  if (modes.every((mode) => mode === 'auto')) return 'on';
  if (modes.every((mode) => mode === 'manual')) return 'off';
  return 'mixed';
}

function workerLabel(view: BackendContinuityView | undefined): string {
  return view?.assignmentTitle ?? view?.displayName ?? 'Managed worker';
}

export function localWorkerView(
  binding: ManagedBinding,
  currentTabId?: number,
  backendView?: BackendContinuityView,
  controlAvailable = false,
): ContinuityWorkerView {
  return {
    workerId: binding.workerId,
    label: workerLabel(backendView),
    hierarchicalName: backendView?.workerName ?? backendView?.displayName ?? null,
    assignmentTitle: backendView?.assignmentTitle ?? null,
    runId: backendView?.runId ?? null,
    runTitle: backendView?.runTitle ?? null,
    conversationState: binding.state,
    conversationUrl: binding.url,
    statusLabel: continuityStatusLabel(backendView, binding.state),
    tabId: binding.tabId,
    currentTab: binding.tabId === currentTabId,
    policyMode: backendView?.policyMode ?? null,
    policySource: backendView?.policySource ?? null,
    runPolicyMode: backendView?.runPolicyMode ?? null,
    workerPolicyOverride: backendView?.workerPolicyOverride ?? false,
    deadmanFallbackEnabled: backendView?.deadmanFallbackEnabled ?? false,
    deadmanThresholdMs: backendView?.deadmanThresholdMs ?? null,
    attemptNumber: backendView?.attemptNumber ?? null,
    autoResumeCount: backendView?.autoResumeCount ?? null,
    lastProgressAt: backendView?.lastProgressAt ?? null,
    blockReason: backendView?.blockReason ?? null,
    pauseReason: backendView?.pauseReason ?? null,
    noProgressAttempts: backendView?.noProgressAttempts ?? null,
    stallWarningAt: backendView?.stallWarningAt ?? null,
    stallPausedAt: backendView?.stallPausedAt ?? null,
    stallState: stallState(backendView),
    recentEvents: backendView?.recentEvents ?? null,
    needsUser: backendView?.needsUser ?? false,
    controlAvailable: controlAvailable && backendView !== undefined && backendView.disposition !== 'terminal',
  };
}


export function deadmanProbeRunIds(
  bindings: Record<string, ManagedBinding>,
  continuityViews: Record<string, BackendContinuityView>,
): string[] {
  const byRun = new Map<string, BackendContinuityView[]>();
  for (const view of Object.values(continuityViews)) {
    if (view.disposition === 'terminal') continue;
    const current = byRun.get(view.runId) ?? [];
    current.push(view);
    byRun.set(view.runId, current);
  }
  const result: string[] = [];
  for (const [runId, views] of byRun) {
    if (!views.some((view) => view.deadmanFallbackEnabled)) continue;
    const allLocallyDead = views.every((view) => {
      const binding = bindings[view.workerId];
      return !binding || binding.state === 'closed' || binding.state === 'error';
    });
    if (allLocallyDead) result.push(runId);
  }
  return result.sort();
}

export function localUiSnapshot(
  bindings: Record<string, ManagedBinding>,
  continuityViews: Record<string, BackendContinuityView>,
  connection: RuntimeStatus | null,
  overlayVisible: boolean,
  currentTabId?: number,
): ContinuityUiSnapshot {
  const connected = connection?.state === 'connected';
  const workers = Object.values(bindings)
    .map((binding) => localWorkerView(binding, currentTabId, continuityViews[binding.workerId], connected))
    .sort((a, b) => a.label.localeCompare(b.label) || a.workerId.localeCompare(b.workerId));
  const backendViews = Object.values(continuityViews);
  const runMap = new Map<string, BackendContinuityView[]>();
  for (const view of backendViews) {
    const current = runMap.get(view.runId) ?? [];
    current.push(view);
    runMap.set(view.runId, current);
  }
  const runs: ContinuityRunView[] = [...runMap.entries()].map(([runId, views]) => ({
    runId,
    title: views[0]?.runTitle ?? runId,
    policyMode: views[0]?.runPolicyMode ?? 'manual',
    workerCount: views.length,
    unfinishedCount: views.filter((view) => view.disposition !== 'terminal').length,
    controlsAvailable: connected,
  })).sort((a, b) => a.title.localeCompare(b.title) || a.runId.localeCompare(b.runId));
  return {
    connection,
    overlayVisible,
    workers,
    runs,
    currentWorker: workers.find((worker) => worker.currentTab) ?? null,
    counts: {
      active: workers.filter((worker) => worker.conversationState !== 'closed').length,
      working: workers.filter((worker) => continuityViews[worker.workerId]?.isWorking ?? worker.conversationState === 'generating').length,
      resuming: workers.filter((worker) => continuityViews[worker.workerId]?.isResuming === true).length,
      needsUser: workers.filter((worker) => continuityViews[worker.workerId]?.needsUser === true).length,
    },
    autoResume: autoResumeSummary(workers),
    capabilities: {
      workerControls: connected && workers.some((worker) => worker.controlAvailable),
      runControls: connected && runs.length > 0,
      runMetadata: backendViews.length > 0,
      policySource: backendViews.length > 0,
      stallMetadata: backendViews.length > 0,
      recentEvents: backendViews.length > 0,
    },
  };
}
