// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { getAssetTypes } from "../core/asset/asset-spec";

// biome-ignore lint/suspicious/noExplicitAny: citty command tree uses dynamic shapes
type AnyCmd = Record<string, any>;

// ── Known flag values ────────────────────────────────────────────────────────

const FLAG_VALUES: Record<string, string[] | (() => string[])> = {
  "--format": ["json", "text", "yaml", "jsonl"],
  "--detail": ["brief", "normal", "full", "summary"],
  "--type": () => [...getAssetTypes(), "any"],
  "--source": ["stash", "registry", "both"],
  "--shell": ["bash"],
};

// ── Command tree walker ──────────────────────────────────────────────────────

interface CommandInfo {
  path: string; // e.g. "registry search"
  subcommands: string[];
  flags: string[];
}

function walkCommandTree(cmd: AnyCmd, parentPath = ""): CommandInfo[] {
  const name = cmd.meta?.name ?? "";
  const currentPath = parentPath ? `${parentPath} ${name}` : name;
  const result: CommandInfo[] = [];

  const subcommands = Object.keys(cmd.subCommands ?? {});
  const flags: string[] = [];

  if (cmd.args) {
    for (const [flagName, arg] of Object.entries(cmd.args as Record<string, AnyCmd>)) {
      if (arg.type === "positional") continue;
      flags.push(`--${flagName}`);
    }
  }

  result.push({ path: currentPath, subcommands, flags });

  if (cmd.subCommands) {
    for (const sub of Object.values(cmd.subCommands as Record<string, AnyCmd>)) {
      result.push(...walkCommandTree(sub, currentPath));
    }
  }

  return result;
}

// ── Bash completion script generator ─────────────────────────────────────────

export function generateBashCompletions(cmd: AnyCmd): string {
  const commands = walkCommandTree(cmd);
  const rootName = cmd.meta?.name ?? "akm";

  // Collect global flags from root command
  const rootInfo = commands.find((c) => c.path === rootName);
  const globalFlags = rootInfo?.flags ?? [];

  // Build the case blocks for subcommand completion
  const caseBlocks: string[] = [];

  for (const info of commands) {
    const allFlags = [...new Set([...info.flags, ...globalFlags])];

    if (info.subcommands.length > 0 || allFlags.length > 0) {
      const matchPath = info.path;
      const subcmdStr = info.subcommands.join(" ");
      const flagStr = allFlags.join(" ");

      caseBlocks.push(`      "${matchPath}")
        if [[ "\${cur}" == -* ]]; then
          COMPREPLY=( $(compgen -W "${flagStr}" -- "\${cur}") )
        else
          COMPREPLY=( $(compgen -W "${subcmdStr}" -- "\${cur}") )
        fi
        return 0
        ;;`);
    }
  }

  // Build flag-value completion cases
  const valueCases: string[] = [];
  for (const [flag, valuesOrFn] of Object.entries(FLAG_VALUES)) {
    const values = typeof valuesOrFn === "function" ? valuesOrFn() : valuesOrFn;
    valueCases.push(`    ${flag})
      COMPREPLY=( $(compgen -W "${values.join(" ")}" -- "\${cur}") )
      return 0
      ;;`);
  }

  const script = `#!/bin/bash
# Bash completion for ${rootName}
# Generated by ${rootName} completions

_${rootName}() {
  local cur prev words cword
  if type _init_completion &>/dev/null; then
    _init_completion || return
  else
    cur="\${COMP_WORDS[COMP_CWORD]}"
    prev="\${COMP_WORDS[COMP_CWORD-1]}"
    words=("\${COMP_WORDS[@]}")
    cword=\${COMP_CWORD}
  fi

  # Complete flag values
  case "\${prev}" in
${valueCases.join("\n")}
  esac

  # Build the command path from COMP_WORDS
  local cmd_path="${rootName}"
  for (( i=1; i < cword; i++ )); do
    case "\${words[i]}" in
      -*) continue ;;
      *) cmd_path="\${cmd_path} \${words[i]}" ;;
    esac
  done

  # Complete based on current command path
  case "\${cmd_path}" in
${caseBlocks.join("\n")}
  esac

  return 0
}

complete -F _${rootName} ${rootName}
`;

  return script;
}

// ── Install ──────────────────────────────────────────────────────────────────

export function installBashCompletions(script: string): string {
  const dest = resolveInstallPath();
  const dir = path.dirname(dest);
  fs.mkdirSync(dir, { recursive: true });
  fs.writeFileSync(dest, script, "utf8");
  return dest;
}

function resolveInstallPath(): string {
  const xdgData = process.env.XDG_DATA_HOME?.trim();
  if (xdgData) {
    return path.join(xdgData, "bash-completion", "completions", "akm");
  }

  const home = os.homedir();

  // Default XDG location
  const defaultXdg = path.join(home, ".local", "share", "bash-completion", "completions", "akm");
  const defaultXdgDir = path.dirname(defaultXdg);
  if (isDir(defaultXdgDir) || isDir(path.dirname(defaultXdgDir))) {
    return defaultXdg;
  }

  // Fallback
  return path.join(home, ".bash_completion.d", "akm");
}

function isDir(p: string): boolean {
  try {
    return fs.statSync(p).isDirectory();
  } catch {
    return false;
  }
}
