import { formatTokens } from './cost';
import './instruments';
import { judgedHeadline } from './headline';
import {
  CHANGE_CLOSED,
  DELIVERY_MISSING,
  NEW_COMMITS_AFTER_CLOSE,
  NO_VERDICT_WORDS,
} from './desktop-view';
import { buildBandView, type BandView } from './model';
import {
  BAND_BODIES,
  layoutShell,
  type BandLayout,
  type BandRow,
  type BandSegment,
  type BodyContext,
  type ShellHeader,
} from './shell';
import { shipRail } from './ship';
import {
  FEATURE_WORDS,
  NO_WORKFLOW,
  readyFeatureRoute,
  routeFromEarlierTurn,
  statusEntryRoute,
  statusEntryRow,
} from './status-entry';
import {
  IDLE_WORDS,
  closedLine,
  minimisedLine,
  needsYouLine,
  visibilityMode,
  type BandVisibility,
} from './visibility';
import { waitingOnHuman } from './attention';
import type { PaqadHudSnapshot, PaqadTone } from './types';

export type { BandLayout, BandRow, BandSegment };

const ELLIPSIS = '…';

export const FEATURE_DEVELOPMENT = 'feature-development';
export const IDLE_HEADLINE = IDLE_WORDS;
export const READY_FEATURE_LINE = `paqad  ${FEATURE_WORDS} · ${IDLE_WORDS}`;

export function rowText(row: BandRow): string {
  return row.map((segment) => segment.text).join('');
}

export function truncateRow(row: BandRow, columns: number): BandRow {
  const width = Math.max(1, Math.floor(columns));
  if ([...rowText(row)].length <= width) return row;
  const out: BandRow = [];
  let room = width - 1;
  for (const segment of row) {
    if (room <= 0) break;
    const chars = [...segment.text];
    const text = chars.slice(0, room).join('');
    out.push({ ...segment, text });
    room -= chars.length;
  }
  out.push({ text: ELLIPSIS });
  return out.filter((segment) => segment.text.length > 0);
}

export function changeChip(snapshot: PaqadHudSnapshot | null): string | null {
  const change = snapshot?.active ? snapshot.change : null;
  if (!change) return null;
  if (change.issue) return /^\d+$/.test(change.issue) ? `#${change.issue}` : change.issue;
  return change.id ? change.id.slice(-6) : null;
}

export function tokensChip(snapshot: PaqadHudSnapshot | null): string | null {
  const cost = snapshot?.active ? snapshot.cost : null;
  if (!cost) return null;
  const tokens = `${formatTokens(cost.tokens.total)} tokens`;
  return cost.scope === 'whole-session' ? `${tokens} · whole session` : tokens;
}

const VERDICT_TONE: Record<'pass' | 'fail' | 'inconclusive', PaqadTone> = {
  pass: 'good',
  fail: 'bad',
  inconclusive: 'warn',
};

const VERDICT_MARK: Record<'pass' | 'fail' | 'inconclusive', string> = {
  pass: '●',
  fail: '✗',
  inconclusive: '◐',
};

const CAUTION_MARK = '▲';

export function closedChangeRow(snapshot: PaqadHudSnapshot, columns: number): BandRow | null {
  const closed = snapshot.closed_change;
  if (!closed) return null;
  const kind = snapshot.verdict?.kind ?? null;
  const word = closed.word ?? NO_VERDICT_WORDS;
  const verdict: BandSegment =
    closed.word && kind
      ? { text: `${VERDICT_MARK[kind]} ${word}`, tone: VERDICT_TONE[kind] }
      : closed.word
        ? { text: word }
        : { text: `${CAUTION_MARK} ${word}`, tone: 'warn' };
  const number = snapshot.ship?.pr_number ?? null;
  const pr: BandSegment =
    number !== null && snapshot.ship?.pr_url
      ? { text: `  PR #${number}` }
      : { text: `  ${CAUTION_MARK} ${DELIVERY_MISSING}`, tone: 'warn' };
  const row: BandRow = [
    { text: 'paqad' },
    { text: `  ${CHANGE_CLOSED} · `, dim: true },
    verdict,
    pr,
  ];
  if (closed.new_commits > 0) {
    row.push({ text: `  ${CAUTION_MARK} ${NEW_COMMITS_AFTER_CLOSE}`, tone: 'warn' });
  }
  return truncateRow(row, columns);
}

export function allGreenNow(snapshot: PaqadHudSnapshot | null): boolean {
  return (
    snapshot?.active === true &&
    snapshot.decisions.pending.length === 0 &&
    shipRail(snapshot).allGreen
  );
}

export function allGreenKey(snapshot: PaqadHudSnapshot | null): string | null {
  if (!allGreenNow(snapshot)) return null;
  const change = snapshot?.change?.dir ?? snapshot?.change?.id ?? 'change';
  return `${change}:${snapshot?.ship?.pr_head ?? 'head'}`;
}

export function bandHeadline(_snapshot: PaqadHudSnapshot, view: BandView): string {
  return view.headline;
}

export function shellHeader(snapshot: PaqadHudSnapshot | null, view: BandView): ShellHeader {
  const active = snapshot?.active === true;
  const verdict = active && !judgedHeadline(snapshot) ? (snapshot?.verdict ?? null) : null;
  return {
    tone: active ? view.tone : 'neutral',
    chip: changeChip(snapshot),
    headline: snapshot && active ? view.headline : IDLE_HEADLINE,
    note: active ? view.note : null,
    gap: active ? view.gap : null,
    activity: active ? view.activity : [],
    verdict: verdict ? { word: verdict.word, tone: VERDICT_TONE[verdict.kind] } : null,
    flags: active ? view.flags : 0,
    lane: active ? (snapshot?.lane ?? null) : null,
    tokens: tokensChip(snapshot),
  };
}

const SHOWN: BandVisibility = { minimised: false, closed: false, hidden: false };

export function layoutBand(
  snapshot: PaqadHudSnapshot | null,
  size: { columns: number; maxRows: number },
  context: BodyContext,
  visibility: BandVisibility = SHOWN,
): BandLayout {
  if (snapshot && (snapshot.route === null || snapshot.route === NO_WORKFLOW)) {
    return { kind: 'none' };
  }
  const entryRoute = statusEntryRoute(snapshot);
  if (snapshot && entryRoute) {
    return {
      kind: 'line',
      row: statusEntryRow(snapshot, entryRoute, context.now, context.turnStartedAt ?? null),
    };
  }
  const view = buildBandView(snapshot, context.now, context.live);
  const mode = visibilityMode(visibility);
  if (mode !== 'band' && waitingOnHuman(snapshot, context.live)) {
    return { kind: 'line', row: needsYouLine() };
  }
  if (snapshot && !snapshot.active) {
    const shown = mode !== 'closed' && mode !== 'hidden';
    const closedRow = shown ? closedChangeRow(snapshot, size.columns) : null;
    if (closedRow) return { kind: 'line', row: closedRow };
    const drawsReady =
      mode !== 'closed' &&
      mode !== 'hidden' &&
      readyFeatureRoute(snapshot) &&
      !routeFromEarlierTurn(snapshot, context.turnStartedAt ?? null);
    return drawsReady
      ? { kind: 'line', row: truncateRow([{ text: READY_FEATURE_LINE }], size.columns) }
      : { kind: 'none' };
  }
  if (mode === 'closed' || mode === 'hidden') {
    return { kind: 'line', row: closedLine(mode, view) };
  }
  const minimised = minimisedLine(snapshot, view, size.columns, context.now);
  if (mode === 'minimised') return { kind: 'line', row: minimised };
  const active = snapshot?.active === true;
  const route = snapshot?.route ?? FEATURE_DEVELOPMENT;
  const renderer = active ? BAND_BODIES[route] : undefined;
  const body = snapshot && renderer ? renderer(snapshot, context) : null;
  const header = { ...shellHeader(snapshot, view), unavailable: context.unavailable === true };
  return layoutShell(header, body, size, minimised);
}

const SPINNER_WORDS: Record<string, string> = {
  ticket_intake: 'Reading the ticket',
  planning: 'Planning',
  specification: 'Writing the spec',
  development: 'Building',
  review: 'Reviewing',
  checks: 'Running checks',
  documentation_sync: 'Syncing the docs',
  delivery: 'Delivering',
};

export function spinnerWord(snapshot: PaqadHudSnapshot | null): string | null {
  if (!snapshot?.active) return null;
  const running = snapshot.stages.find((stage) => stage.state === 'running');
  return running ? (SPINNER_WORDS[running.stage] ?? null) : null;
}

export function laneMode(snapshot: PaqadHudSnapshot | null): string | null {
  return snapshot?.active && snapshot.lane ? `paqad ${snapshot.lane} lane` : null;
}
