import type { ConversationState } 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 interface RecentContinuityEvent {
  type: string;
  timestamp: string;
  detail?: string;
}

export interface BackendContinuityView {
  workerId: string;
  displayName?: string;
  assignmentTitle?: string;
  runId?: string;
  runTitle?: string;
  workerName?: string;
  disposition: 'running' | 'awaiting_execution' | 'awaiting_human' | 'awaiting_dependency' | 'paused' | 'terminal';
  policyMode: 'auto' | 'manual';
  policySource?: 'worker' | 'run' | 'default';
  attemptNumber: number | null;
  autoResumeCount: number;
  lastProgressAt: string | null;
  blockReason?: string | null;
  pauseReason?: 'user' | 'stall' | 'error' | null;
  noProgressAttempts?: number | null;
  stallWarningAt?: string | null;
  stallPausedAt?: string | null;
  recentEvents?: RecentContinuityEvent[];
  needsUser: boolean;
  isWorking: boolean;
  isResuming: boolean;
  updatedAt?: string;
  stallMetadataAvailable?: boolean;
}

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;
  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: RecentContinuityEvent[] | null;
  needsUser: boolean;
  controlAvailable: boolean;
}

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

export interface ContinuityUiSnapshot {
  connection: RuntimeStatus | null;
  overlayVisible: boolean;
  workers: ContinuityWorkerView[];
  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 type UiRequest = UiSnapshotRequest | UiWorkerActionRequest | UiPolicyActionRequest;

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?.stallMetadataAvailable) 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,
    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 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);
  return {
    connection,
    overlayVisible,
    workers,
    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: false,
      runMetadata: backendViews.some((view) => Boolean(view.runId || view.runTitle)),
      policySource: backendViews.some((view) => Boolean(view.policySource)),
      stallMetadata: backendViews.some((view) => view.stallMetadataAvailable === true),
      recentEvents: backendViews.some((view) => Array.isArray(view.recentEvents)),
    },
  };
}
