/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { readFile, writeFile } from 'node:fs/promises';

import type { KeyBinding } from '../packages/cli/src/ui/key/keyBindings.js';
import {
  commandCategories,
  commandDescriptions,
  defaultKeyBindingConfig,
  Command,
  getPlatformUndoBindings,
  getPlatformRedoBindings,
} from '../packages/cli/src/ui/key/keyBindings.js';
import {
  formatWithPrettier,
  injectBetweenMarkers,
  normalizeForCompare,
} from './utils/autogen.js';

const START_MARKER = '<!-- KEYBINDINGS-AUTOGEN:START -->';
const END_MARKER = '<!-- KEYBINDINGS-AUTOGEN:END -->';
const OUTPUT_RELATIVE_PATH = ['docs', 'reference', 'keyboard-shortcuts.md'];

import { formatKeyBinding } from '../packages/cli/src/ui/key/keybindingUtils.js';

export interface KeybindingDocCommand {
  command: string;
  description: string;
  bindings: readonly KeyBinding[];
}

export interface KeybindingDocSection {
  title: string;
  commands: readonly KeybindingDocCommand[];
}

export async function main(argv = process.argv.slice(2)) {
  const checkOnly = argv.includes('--check');

  const repoRoot = path.resolve(
    path.dirname(fileURLToPath(import.meta.url)),
    '..',
  );
  const docPath = path.join(repoRoot, ...OUTPUT_RELATIVE_PATH);

  const sections = buildDefaultDocSections();
  const generatedBlock = renderDocumentation(sections);
  const currentDoc = await readFile(docPath, 'utf8');
  const injectedDoc = injectBetweenMarkers({
    document: currentDoc,
    startMarker: START_MARKER,
    endMarker: END_MARKER,
    newContent: generatedBlock,
    paddingBefore: '\n\n',
    paddingAfter: '\n',
  });
  const updatedDoc = await formatWithPrettier(injectedDoc, docPath);

  if (normalizeForCompare(updatedDoc) === normalizeForCompare(currentDoc)) {
    if (!checkOnly) {
      console.log('Keybinding documentation already up to date.');
    }
    return;
  }

  if (checkOnly) {
    console.error(
      'Keybinding documentation is out of date. Run `npm run docs:keybindings` to regenerate.',
    );
    process.exitCode = 1;
    return;
  }

  await writeFile(docPath, updatedDoc, 'utf8');
  console.log('Keybinding documentation regenerated.');
}

export function buildDefaultDocSections(): readonly KeybindingDocSection[] {
  return commandCategories.map((category) => ({
    title: category.title,
    commands: category.commands.map((command) => {
      // For UNDO and REDO, we want to show all platform variants in the docs
      if (command === Command.UNDO) {
        return {
          command: command,
          description: commandDescriptions[command],
          bindings: getMergedPlatformBindings(getPlatformUndoBindings),
        };
      }
      if (command === Command.REDO) {
        return {
          command: command,
          description: commandDescriptions[command],
          bindings: getMergedPlatformBindings(getPlatformRedoBindings),
        };
      }

      return {
        command: command,
        description: commandDescriptions[command],
        bindings: defaultKeyBindingConfig.get(command) ?? [],
      };
    }),
  }));
}

function getMergedPlatformBindings(
  getBindings: (platform: string) => readonly KeyBinding[],
): readonly KeyBinding[] {
  const win32 = getBindings('win32');
  const darwin = getBindings('darwin');
  const linux = getBindings('linux');

  const all = [...win32, ...darwin, ...linux];
  const seen = new Set<string>();
  const unique: KeyBinding[] = [];

  for (const b of all) {
    const key = `${b.name}-${b.ctrl}-${b.shift}-${b.alt}-${b.cmd}`;
    if (!seen.has(key)) {
      seen.add(key);
      unique.push(b);
    }
  }

  return unique;
}

export function renderDocumentation(
  sections: readonly KeybindingDocSection[],
): string {
  const renderedSections = sections.map((section) => {
    const rows = section.commands.map((command) => {
      const formattedBindings = formatBindings(command.bindings);
      const keysCell = formattedBindings.join('<br />');
      return `| \`${command.command}\` | ${command.description} | ${keysCell} |`;
    });

    return [
      `#### ${section.title}`,
      '',
      '| Command | Action | Keys |',
      '| --- | --- | --- |',
      ...rows,
    ].join('\n');
  });

  return renderedSections.join('\n\n');
}

function formatBindings(bindings: readonly KeyBinding[]): string[] {
  const seen = new Set<string>();
  const results: string[] = [];

  for (const binding of bindings) {
    const label = formatKeyBinding(binding, 'default');
    if (label && !seen.has(label)) {
      seen.add(label);
      results.push(`\`${label}\``);
    }
  }

  return results;
}

if (process.argv[1]) {
  const entryUrl = pathToFileURL(path.resolve(process.argv[1])).href;
  if (entryUrl === import.meta.url) {
    await main();
  }
}
