import browser from 'webextension-polyfill';
import { autoResumeLabel, blockReasonLabel, policyLabel, relativeAge, stallDescription, workerSecondaryName } from './presentation.js';
import { UI_MESSAGE_KIND, type ContinuityRunView, type ContinuityUiSnapshot, type ContinuityWorkerView, type UiAction } from './ui-model.js';

function byId<T extends HTMLElement>(id: string): T {
  const element = document.getElementById(id);
  if (!element) throw new Error(`Missing dashboard element #${id}.`);
  return element as T;
}

async function snapshot(): Promise<ContinuityUiSnapshot> {
  return browser.runtime.sendMessage({ kind: UI_MESSAGE_KIND, action: 'snapshot' }) as Promise<ContinuityUiSnapshot>;
}

async function performAction(action: UiAction, worker: ContinuityWorkerView, enabled?: boolean): Promise<void> {
  if (action === 'stop' && !window.confirm(`Stop ${worker.label}? This interrupts the logical worker and prevents future automatic continuation.`)) return;
  if (action === 'continue-anyway' && !window.confirm(`Continue ${worker.label} anyway? This explicitly overrides the current backend block/stall guard for one manual continuation attempt.`)) return;
  const response = await browser.runtime.sendMessage({
    kind: UI_MESSAGE_KIND,
    action,
    workerId: worker.workerId,
    ...(action === 'set-auto-resume' ? { enabled: Boolean(enabled) } : {}),
  }) as { ok?: boolean; error?: string };
  byId<HTMLElement>('action-status').textContent = response?.ok
    ? 'Control applied by the orchestrator backend.'
    : response?.error ?? 'The control could not be applied.';
  if (response?.ok) await render();
}

async function performRunAction(action: 'pause-run' | 'resume-run' | 'stop-run' | 'set-run-auto-resume', run: ContinuityRunView, enabled?: boolean): Promise<void> {
  if (action === 'stop-run' && !window.confirm(`Stop ${run.title}? This cancels every unfinished worker in the run.`)) return;
  const response = await browser.runtime.sendMessage({
    kind: UI_MESSAGE_KIND,
    action,
    runId: run.runId,
    ...(action === 'set-run-auto-resume' ? { enabled: Boolean(enabled) } : {}),
  }) as { ok?: boolean; error?: string };
  byId<HTMLElement>('action-status').textContent = response?.ok
    ? 'Run control applied by the orchestrator backend.'
    : response?.error ?? 'The run control could not be applied.';
  if (response?.ok) await render();
}

async function openConversation(worker: ContinuityWorkerView): Promise<void> {
  const tab = await browser.tabs.get(worker.tabId).catch(() => null);
  if (!tab) {
    byId<HTMLElement>('action-status').textContent = 'The managed conversation tab is no longer available.';
    return;
  }
  await browser.tabs.update(worker.tabId, { active: true });
  if (tab.windowId !== undefined) await browser.windows.update(tab.windowId, { focused: true });
}

function button(label: string, onClick: () => void, options: { destructive?: boolean; disabled?: boolean; title?: string | undefined } = {}): HTMLButtonElement {
  const element = document.createElement('button');
  element.type = 'button';
  element.textContent = label;
  element.className = options.destructive ? 'control destructive' : 'control';
  element.disabled = options.disabled ?? false;
  if (options.title) element.title = options.title;
  element.addEventListener('click', onClick);
  return element;
}

function workerActions(worker: ContinuityWorkerView): HTMLElement {
  const actions = document.createElement('div');
  actions.className = 'worker-actions';
  const disabled = !worker.controlAvailable;
  const unavailable = disabled ? 'Backend continuity controls are unavailable for this worker.' : undefined;
  actions.append(
    button('Pause', () => { void performAction('pause', worker); }, { disabled, title: unavailable }),
    button('Continue now', () => { void performAction('continue-now', worker); }, { disabled, title: unavailable }),
    button(worker.policyMode === 'auto' ? 'Auto Resume off' : 'Auto Resume on', () => {
      void performAction('set-auto-resume', worker, worker.policyMode !== 'auto');
    }, { disabled: disabled || worker.policyMode === null, title: worker.policyMode === null ? 'Backend policy is unavailable.' : unavailable }),
    button('Stop worker', () => { void performAction('stop', worker); }, { destructive: true, disabled, title: unavailable }),
  );
  return actions;
}

function policyBadge(worker: ContinuityWorkerView): HTMLElement {
  const badge = document.createElement('span');
  badge.className = 'policy-badge';
  badge.dataset.source = worker.policySource ?? 'unavailable';
  badge.textContent = policyLabel(worker);
  return badge;
}

function diagnostics(worker: ContinuityWorkerView): HTMLElement {
  const details = document.createElement('details');
  details.className = 'diagnostics';
  const summary = document.createElement('summary');
  summary.textContent = 'Diagnostics';
  const list = document.createElement('dl');
  const values: Array<[string, string]> = [
    ['Worker ID', worker.workerId],
    ['Run ID', worker.runId ?? 'Unavailable'],
    ['Tab ID', String(worker.tabId)],
    ['Conversation URL', worker.conversationUrl],
    ['Browser state', worker.conversationState],
  ];
  for (const [label, value] of values) {
    const dt = document.createElement('dt');
    dt.textContent = label;
    const dd = document.createElement('dd');
    dd.textContent = value;
    list.append(dt, dd);
  }
  details.append(summary, list);
  return details;
}

function workerCard(worker: ContinuityWorkerView): HTMLElement {
  const card = document.createElement('article');
  card.className = 'worker-card';
  if (worker.needsUser) card.dataset.needsUser = 'true';
  const header = document.createElement('div');
  header.className = 'worker-card-header';
  const titleBox = document.createElement('div');
  const title = document.createElement('h3');
  title.textContent = worker.label;
  titleBox.append(title);
  const secondary = workerSecondaryName(worker);
  if (secondary) {
    const hierarchy = document.createElement('p');
    hierarchy.className = 'hierarchy';
    hierarchy.textContent = secondary;
    titleBox.append(hierarchy);
  }
  const state = document.createElement('span');
  state.className = 'state-badge';
  state.textContent = worker.statusLabel;
  header.append(titleBox, state);

  const grid = document.createElement('dl');
  grid.className = 'worker-grid';
  const fields: Array<[string, Node]> = [
    ['Run', document.createTextNode(worker.runTitle ?? (worker.runId ? 'Run title unavailable' : 'Run metadata unavailable'))],
    ['Policy', policyBadge(worker)],
    ['Attempt', document.createTextNode(worker.attemptNumber === null ? 'Unavailable' : String(worker.attemptNumber))],
    ['Auto resumes', document.createTextNode(worker.autoResumeCount === null ? 'Unavailable' : String(worker.autoResumeCount))],
    ['Last progress', document.createTextNode(relativeAge(worker.lastProgressAt))],
    ['Block reason', document.createTextNode(blockReasonLabel(worker))],
  ];
  for (const [label, value] of fields) {
    const dt = document.createElement('dt');
    dt.textContent = label;
    const dd = document.createElement('dd');
    dd.append(value);
    grid.append(dt, dd);
  }

  const conversation = button('Open conversation', () => { void openConversation(worker); });
  conversation.classList.add('open-conversation');
  card.append(header, grid, conversation, workerActions(worker), diagnostics(worker));
  return card;
}

function stallCard(worker: ContinuityWorkerView): HTMLElement {
  const card = document.createElement('article');
  card.className = 'stall-card';
  const heading = document.createElement('h3');
  heading.textContent = worker.stallState === 'paused' ? 'Worker paused for stall' : 'Worker appears stuck';
  const text = document.createElement('p');
  text.textContent = stallDescription(worker);
  const actions = document.createElement('div');
  actions.className = 'stall-actions';
  const disabled = !worker.controlAvailable;
  actions.append(
    button('Continue anyway', () => { void performAction('continue-anyway', worker); }, { disabled }),
    button('Pause', () => { void performAction('pause', worker); }, { disabled }),
    button('Open conversation', () => { void openConversation(worker); }),
    button('Stop worker', () => { void performAction('stop', worker); }, { destructive: true, disabled }),
  );
  card.append(heading, text, actions);
  return card;
}

function renderRunControls(view: ContinuityUiSnapshot): void {
  const controls = byId<HTMLElement>('run-controls');
  controls.replaceChildren();
  if (view.runs.length === 0) {
    byId<HTMLElement>('run-control-note').textContent = 'No active run metadata has been published by the orchestrator backend yet.';
    byId<HTMLElement>('run-metadata-note').textContent = 'Run identity and controls appear after a managed worker publishes authoritative continuity state.';
    return;
  }

  for (const run of view.runs) {
    const row = document.createElement('article');
    row.className = 'run-control-row';
    const heading = document.createElement('div');
    heading.className = 'run-control-heading';
    const title = document.createElement('strong');
    title.textContent = run.title;
    const detail = document.createElement('span');
    detail.className = 'muted';
    detail.textContent = `${run.unfinishedCount} unfinished / ${run.workerCount} observed · Run default ${run.policyMode === 'auto' ? 'Auto Resume On' : 'Auto Resume Off'}`;
    heading.append(title, detail);
    const actions = document.createElement('div');
    actions.className = 'worker-actions';
    const disabled = !run.controlsAvailable;
    const unavailable = disabled ? 'Backend run controls are unavailable while the executor is offline.' : undefined;
    actions.append(
      button(run.policyMode === 'auto' ? 'Auto Resume unfinished: on' : 'Auto Resume unfinished: off', () => {
        void performRunAction('set-run-auto-resume', run, run.policyMode !== 'auto');
      }, { disabled, title: unavailable }),
      button('Pause all after current turns', () => { void performRunAction('pause-run', run); }, { disabled, title: unavailable }),
      button('Resume all unfinished', () => { void performRunAction('resume-run', run); }, { disabled, title: unavailable }),
      button('Stop run', () => { void performRunAction('stop-run', run); }, { destructive: true, disabled, title: unavailable }),
    );
    row.append(heading, actions);
    controls.append(row);
  }
  byId<HTMLElement>('run-control-note').textContent = 'Run actions are sent once to the orchestrator backend; the extension does not simulate run authority by iterating tabs.';
  byId<HTMLElement>('run-metadata-note').textContent = `${view.runs.length} authoritative run${view.runs.length === 1 ? '' : 's'} observed in this browser session.`;
}

function renderWorkers(view: ContinuityUiSnapshot): void {
  const container = byId<HTMLElement>('workers');
  container.replaceChildren();
  if (view.workers.length === 0) {
    const empty = document.createElement('p');
    empty.className = 'empty';
    empty.textContent = 'No managed workers are currently known to this browser.';
    container.append(empty);
    return;
  }
  for (const worker of view.workers) container.append(workerCard(worker));
}

function renderStalls(view: ContinuityUiSnapshot): void {
  const container = byId<HTMLElement>('stall-cards');
  container.replaceChildren();
  if (!view.capabilities.stallMetadata) {
    const unavailable = document.createElement('article');
    unavailable.className = 'capability-card';
    const heading = document.createElement('h3');
    heading.textContent = 'Stall telemetry unavailable';
    const text = document.createElement('p');
    text.textContent = 'The backend currently does not publish stall warning/pause metadata to the extension. Stall recovery is therefore not guessed from attempt counts or elapsed browser time.';
    unavailable.append(heading, text);
    container.append(unavailable);
    return;
  }
  const stalled = view.workers.filter((worker) => worker.stallState === 'warning' || worker.stallState === 'paused');
  if (stalled.length === 0) {
    const clear = document.createElement('p');
    clear.className = 'empty';
    clear.textContent = 'No stall warnings reported.';
    container.append(clear);
    return;
  }
  for (const worker of stalled) container.append(stallCard(worker));
}

async function render(): Promise<void> {
  const view = await snapshot();
  const connection = byId<HTMLElement>('connection');
  connection.textContent = view.connection?.state === 'connected' ? 'Live' : view.connection?.state ?? 'Offline';
  connection.dataset.state = view.connection?.state ?? 'unknown';
  byId<HTMLElement>('connection-detail').textContent = view.connection?.detail ?? 'Executor status unavailable.';
  byId<HTMLElement>('auto-resume-state').textContent = autoResumeLabel(view.autoResume);
  byId<HTMLElement>('active-count').textContent = String(view.counts.active);
  byId<HTMLElement>('working-count').textContent = String(view.counts.working);
  byId<HTMLElement>('resuming-count').textContent = String(view.counts.resuming);
  byId<HTMLElement>('needs-you-count').textContent = String(view.counts.needsUser);
  renderRunControls(view);
  renderWorkers(view);
  renderStalls(view);
}

byId<HTMLButtonElement>('refresh').addEventListener('click', () => { void render(); });
byId<HTMLButtonElement>('settings').addEventListener('click', () => { void browser.runtime.openOptionsPage(); });
browser.storage.onChanged.addListener(() => { void render(); });
void render().catch((error) => {
  byId<HTMLElement>('action-status').textContent = error instanceof Error ? error.message : 'Runtime dashboard state could not be loaded.';
});
