import { slash } from './command-name';
import { costLines } from './cost';
import {
  buildBandView,
  flagPill,
  headlineAfterState,
  stageLabel,
  stageSegments,
  type BandView,
} from './model';
import {
  CHANGE_CLOSED,
  DELIVERY_MISSING,
  NEW_COMMITS_AFTER_CLOSE,
  NO_VERDICT_WORDS,
} from './desktop-view';
import { shipRail, STEP_WORDS } from './ship';
import { hiddenReply, SHOWN_AGAIN } from './visibility';
import { linesText, recordLines, shipsLines, trustLines } from './tabs';
import type { PaqadFindingsScan, PaqadHudSnapshot, PaqadStandDown } from './types';

export const SUBCOMMANDS = [
  'status',
  'decisions',
  'rules',
  'receipt',
  'cost',
  'trust',
  'ships',
  'delivery',
  'record',
  'show',
  'hide',
] as const;

const ALIASES: Readonly<Record<string, string>> = { delivery: 'ships' };

export const COMMAND_DESCRIPTION =
  'Where the paqad change stands: status, decisions, rules, receipt, cost, trust, delivery, record, or show or hide the band';
export const ARGUMENT_HINT = `[${SUBCOMMANDS.join('|')}]`;

export function subcommandOf(args: string): string {
  const word = args.trim().split(/\s+/)[0]?.toLowerCase() ?? '';
  return ALIASES[word] ?? word;
}

export interface CommandInput {
  args: string;
  standDown: PaqadStandDown | null;
  snapshot: PaqadHudSnapshot | null;
  receipt: string | null;
  now: number;
  repoDecisions?: readonly RepoDecision[];
}

function help(): string {
  const listed = SUBCOMMANDS.filter((sub) => ALIASES[sub] === undefined).map((sub) => slash(sub));
  return `Try ${listed.slice(0, -1).join(', ')} or ${listed.at(-1)}.`;
}

const STAND_DOWN_WORDS: Record<PaqadStandDown, string> = {
  'view-off': 'the paqad HUD is turned off in the plugin settings',
  'not-onboarded': 'this project was never onboarded',
  remote: 'this session runs in the cloud',
  disabled: 'paqad is turned off for this project',
  'old-engine': 'this Claude Code is older than 2.1.301',
};

export function notActiveText(reason: PaqadStandDown): string {
  return `paqad is not active here: ${STAND_DOWN_WORDS[reason]}.`;
}

const NO_RECORD = `Not yet. paqad has no record for this session.`;

function stageLine(view: BandView): string {
  return `Stage: ${view.stages
    .map((segment) =>
      segment.state === 'running' && view.clock
        ? `${segment.label} running ${view.clock}`
        : `${segment.label} ${segment.state}`,
    )
    .join(', ')}.`;
}

function shipLine(snapshot: PaqadHudSnapshot): string {
  return `Ship: ${shipRail(snapshot)
    .steps.map((step) => `${step.label} ${STEP_WORDS[step.state]}`)
    .join(', ')}.`;
}

function stateLine(view: BandView): string {
  const pill = flagPill(view.flags);
  const rest = headlineAfterState(view);
  return `${view.state}${pill ? ` (${pill})` : ''}.${rest ? ` ${rest}` : ''}`;
}

function overview(snapshot: PaqadHudSnapshot, now: number): string {
  const view = buildBandView(snapshot, now);
  return [stateLine(view), stageLine(view), shipLine(snapshot), view.cost, help()].join('\n');
}

function closedStatusLine(
  snapshot: PaqadHudSnapshot,
  closed: NonNullable<PaqadHudSnapshot['closed_change']>,
): string {
  const number = snapshot.ship?.pr_number ?? null;
  const parts = [`${CHANGE_CLOSED}: ${closed.word ?? NO_VERDICT_WORDS}`];
  parts.push(number !== null && snapshot.ship?.pr_url ? `PR #${number}` : DELIVERY_MISSING);
  if (closed.new_commits > 0) parts.push(NEW_COMMITS_AFTER_CLOSE);
  return `${parts.join(', ')}.`;
}

function status(snapshot: PaqadHudSnapshot, now: number): string {
  const view = buildBandView(snapshot, now);
  const lines = [stateLine(view)];
  if (snapshot.active && view.note) lines.push(`Note: ${view.note}.`);
  if (snapshot.active && view.gap) lines.push(`${view.gap}.`);
  const change = snapshot.change;
  const closed = !snapshot.active ? snapshot.closed_change : undefined;
  if ((snapshot.active || closed) && change) {
    const name = [change.issue ? `#${change.issue}` : null, change.title].filter(Boolean).join(' ');
    lines.push(`Change: ${name || change.id || 'untitled'}.`);
    if (change.branch) lines.push(`Branch: ${change.branch}.`);
  }
  if (closed) lines.push(closedStatusLine(snapshot, closed));
  if (snapshot.lane) {
    lines.push(`Lane: ${snapshot.lane}${snapshot.lane_reason ? `, ${snapshot.lane_reason}` : ''}.`);
  }
  if (snapshot.paused.length > 0) {
    lines.push(
      `Paused: ${snapshot.paused.length} change${snapshot.paused.length === 1 ? '' : 's'}.`,
    );
  }
  for (const segment of stageSegments(snapshot)) {
    const clock = segment.state === 'running' && view.clock ? ` ${view.clock}` : '';
    lines.push(`  ${segment.label}: ${segment.state}${clock}`);
  }
  for (const flag of snapshot.flags) lines.push(`Flag: ${flag.text}`);
  if (snapshot.verdict) {
    lines.push(`Verdict: ${snapshot.verdict.word}.`);
    for (const reason of snapshot.verdict.reasons) lines.push(`  ${reason}`);
  }
  return lines.join('\n');
}

export interface RepoDecision {
  id: string;
  category: string;
  question: string;
}

function decisions(snapshot: PaqadHudSnapshot, repo: readonly RepoDecision[] | undefined): string {
  const pending = repo ?? snapshot.decisions.pending;
  const where = repo ? ' in this repo' : '';
  const done = `${snapshot.decisions.resolved} resolved for this change.`;
  if (pending.length === 0) return `No decision is waiting${where}. ${done}`;
  return [
    `${pending.length} decision${pending.length === 1 ? '' : 's'} waiting${where}. ${done}`,
    ...pending.map((d) => `  ${d.id} (${d.category}): ${d.question || 'no question recorded'}`),
  ].join('\n');
}

export function readRepoDecision(name: string, text: string | null): RepoDecision {
  const fallback = name.replace(/\.json$/, '');
  let packet: Record<string, unknown> = {};
  try {
    const parsed: unknown = text === null ? null : JSON.parse(text);
    if (typeof parsed === 'object' && parsed !== null) packet = parsed as Record<string, unknown>;
  } catch {
    packet = {};
  }
  const word = (key: string) => (typeof packet[key] === 'string' ? (packet[key] as string) : null);
  return {
    id: word('id') ?? word('decision_id') ?? fallback,
    category: word('category') ?? 'unknown',
    question: word('question') ?? word('title') ?? '',
  };
}

function scanLine(label: string, scan: PaqadFindingsScan | undefined): string {
  if (!scan || scan.status === 'did not run') return `${label}: did not run.`;
  if (scan.status === 'clean') return `${label}: none open.`;
  return `${label}: ${scan.total ?? 0} open${scan.blocking ? ', blocking' : ''}.`;
}

function rules(snapshot: PaqadHudSnapshot): string {
  if (!snapshot.active) return 'No change is in progress, so no rules are loaded for one.';
  const lines = [
    snapshot.rules.recorded
      ? `Rules loaded for this change: ${snapshot.rules.applicable ?? 0} apply${
          snapshot.rules.loaded_at ? `, loaded ${snapshot.rules.loaded_at}` : ''
        }.`
      : 'The rules for this change are not loaded yet.',
    scanLine('Rule findings', snapshot.findings?.rules),
    scanLine('Near-copies', snapshot.findings?.near_copies),
  ];
  return lines.join('\n');
}

function cost(snapshot: PaqadHudSnapshot): string {
  if (!snapshot.active) return 'No change is in progress, so there is no cost to show.';
  if (!snapshot.cost) {
    return 'Cost: not yet. paqad counts it from the transcripts at the end of each turn.';
  }
  return costLines(snapshot.cost, { lane: snapshot.lane, stageLabel }).join('\n');
}

export const NO_RECEIPT = 'No receipt yet. paqad writes one at the end of each turn.';

function receipt(text: string | null): string {
  const body = text?.trim();
  return body ? body : NO_RECEIPT;
}

export function runPaqadCommand(input: CommandInput): { text: string } {
  if (input.standDown) return { text: notActiveText(input.standDown) };
  const sub = subcommandOf(input.args);
  if (sub === 'receipt') return { text: receipt(input.receipt) };
  if (sub === 'show') return { text: SHOWN_AGAIN };
  if (sub === 'hide') return { text: hiddenReply() };
  if (sub !== '' && !(SUBCOMMANDS as readonly string[]).includes(sub)) {
    return { text: `There is no ${slash(sub)}. ${help()}` };
  }
  const snapshot = input.snapshot;
  if (!snapshot) return { text: `${NO_RECORD} ${help()}` };
  if (sub === 'status') return { text: status(snapshot, input.now) };
  if (sub === 'decisions') return { text: decisions(snapshot, input.repoDecisions) };
  if (sub === 'rules') return { text: rules(snapshot) };
  if (sub === 'cost') return { text: cost(snapshot) };
  if (sub === 'trust') return { text: linesText(trustLines(snapshot)) };
  if (sub === 'ships') return { text: linesText(shipsLines(snapshot, input.now)) };
  if (sub === 'record') return { text: linesText(recordLines(snapshot)) };
  return { text: overview(snapshot, input.now) };
}
