import { createUsageError } from "./errors";
import {
  CLI_COMMANDS,
  VALID_EQUALS_FLAGS,
  VALID_FLAGS
} from "../public-surface/generated-manifest";
import type { PublicSurfaceCliCommandName } from "../public-surface/source";

const CLI_COMMAND_SET = new Set<string>(CLI_COMMANDS);

export { CLI_COMMANDS, VALID_EQUALS_FLAGS, VALID_FLAGS };

export type CliCommand = PublicSurfaceCliCommandName;
export type InstallMode = "global" | "local";
export type SkillsMode = "global" | "local" | "none";
export type OutputFormat = "text" | "json" | "stream-json";
export type TransportMode = "relay" | "native";

export interface ParsedArgs {
  command: CliCommand;
  mode?: InstallMode;
  withConfig: boolean;
  noPrompt: boolean;
  noInteractive: boolean;
  quiet: boolean;
  outputFormat: OutputFormat;
  transport: TransportMode;
  skillsMode: SkillsMode;
  fullInstall: boolean;
  rawArgs: string[];
}

const SHORT_FLAGS: Record<string, string> = {
  "-g": "--global",
  "-l": "--local",
  "-u": "--update",
  "-h": "--help",
  "-v": "--version",
  "-f": "--full"
};

function expandShortFlags(args: string[]): string[] {
  return args.map((arg) => SHORT_FLAGS[arg] ?? arg);
}

function parseSkillsMode(args: string[]): SkillsMode {
  const hasLocal = args.includes("--skills-local");
  const hasGlobal = args.includes("--skills-global");
  if (hasLocal && hasGlobal) {
    throw createUsageError("Choose either --skills-local or --skills-global.");
  }
  if (args.includes("--no-skills")) {
    return "none";
  }
  if (hasLocal) {
    return "local";
  }
  if (hasGlobal) {
    return "global";
  }
  return "global";
}

function parseOutputFormat(args: string[]): OutputFormat {
  const outputFlag = args.find((arg) => arg.startsWith("--output-format"));
  if (!outputFlag) {
    return "text";
  }

  let value: string | undefined;
  if (outputFlag.includes("=")) {
    value = outputFlag.split("=", 2)[1];
  } else {
    const index = args.indexOf(outputFlag);
    value = index >= 0 ? args[index + 1] : undefined;
  }

  if (value === "text" || value === "json" || value === "stream-json") {
    return value;
  }

  throw createUsageError(`Invalid --output-format: ${value ?? "missing"}`);
}

function parseTransport(args: string[]): TransportMode {
  const transportFlag = args.find((arg) => arg.startsWith("--transport"));
  if (!transportFlag) {
    return "relay";
  }

  let value: string | undefined;
  if (transportFlag.includes("=")) {
    value = transportFlag.split("=", 2)[1];
  } else {
    const index = args.indexOf(transportFlag);
    value = index >= 0 ? args[index + 1] : undefined;
  }

  if (value === "relay" || value === "native") {
    return value;
  }

  throw createUsageError(`Invalid --transport: ${value ?? "missing"}`);
}

const VALID_FLAG_SET = new Set<string>(VALID_FLAGS);
const VALID_EQUALS_FLAG_SET = new Set<string>(VALID_EQUALS_FLAGS);
const SIGNED_VALUE_FLAG_SET = new Set<string>(["--dy"]);
const PASSTHROUGH_VALUE_FLAG_SET = new Set<string>(["--flag"]);
const SIGNED_INTEGER_VALUE = /^-\d+$/;

function shouldSkipValueToken(flag: string, value: string | undefined): boolean {
  if (PASSTHROUGH_VALUE_FLAG_SET.has(flag) && value !== undefined) {
    return true;
  }
  if (!VALID_EQUALS_FLAG_SET.has(flag) || value === undefined) {
    return false;
  }
  if (!value.startsWith("-")) {
    return true;
  }
  return SIGNED_VALUE_FLAG_SET.has(flag) && SIGNED_INTEGER_VALUE.test(value);
}

export function parseArgs(argv: string[]): ParsedArgs {
  let args = expandShortFlags(argv.slice(2));
  let commandOverride: CliCommand | null = null;

  if (args[0] && !args[0].startsWith("-")) {
    const candidate = args[0];
    if (CLI_COMMAND_SET.has(candidate)) {
      commandOverride = candidate as CliCommand;
      args = args.slice(1);
    } else {
      throw createUsageError(`Unknown command: ${candidate}`);
    }
  }
  const hasGlobal = args.includes("--global");
  const hasLocal = args.includes("--local");
  if (hasGlobal && hasLocal) {
    throw createUsageError("Choose either --global or --local.");
  }

  const skillsMode = parseSkillsMode(args);
  const fullInstall = args.includes("--full");
  const outputFormat = parseOutputFormat(args);
  const transport = commandOverride === "annotate" ? "relay" : parseTransport(args);

  if (commandOverride === "help" || args.includes("--help") || args.includes("-h")) {
    return {
      command: "help",
      withConfig: false,
      noPrompt: false,
      noInteractive: false,
      quiet: false,
      outputFormat,
      transport,
      skillsMode,
      fullInstall,
      rawArgs: args
    };
  }

  if (commandOverride === "version" || args.includes("--version") || args.includes("-v")) {
    return {
      command: "version",
      withConfig: false,
      noPrompt: false,
      noInteractive: false,
      quiet: false,
      outputFormat,
      transport,
      skillsMode,
      fullInstall,
      rawArgs: args
    };
  }

  if (commandOverride === "update" || args.includes("--update")) {
    const mode = args.includes("--global") ? "global" : args.includes("--local") ? "local" : undefined;
    return {
      command: "update",
      mode,
      withConfig: false,
      noPrompt: false,
      noInteractive: false,
      quiet: false,
      outputFormat,
      transport,
      skillsMode,
      fullInstall,
      rawArgs: args
    };
  }

  if (commandOverride === "uninstall" || args.includes("--uninstall")) {
    const mode = args.includes("--global") ? "global" : args.includes("--local") ? "local" : undefined;
    const noPrompt = args.includes("--no-prompt") || args.includes("--no-interactive");
    return {
      command: "uninstall",
      mode,
      withConfig: false,
      noPrompt,
      noInteractive: noPrompt,
      quiet: args.includes("--quiet"),
      outputFormat,
      transport,
      skillsMode,
      fullInstall,
      rawArgs: args
    };
  }

  const withConfig = args.includes("--with-config") || fullInstall;
  const noPrompt = args.includes("--no-prompt") || args.includes("--no-interactive");
  const noInteractive = args.includes("--no-interactive") || noPrompt;
  const quiet = args.includes("--quiet");

  let mode: InstallMode | undefined;
  if (args.includes("--global")) {
    mode = "global";
  } else if (args.includes("--local")) {
    mode = "local";
  } else if (noPrompt) {
    mode = "global";
  }

  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index] ?? "";
    if (arg.startsWith("--") && !VALID_FLAG_SET.has(arg)) {
      if (arg.includes("=")) {
        const baseFlag = arg.split("=", 2)[0] ?? "";
        if (VALID_EQUALS_FLAG_SET.has(baseFlag)) {
          continue;
        }
      }
      throw createUsageError(`Unknown flag: ${arg}`);
    }
    if (arg.startsWith("-") && !arg.startsWith("--") && !SHORT_FLAGS[arg]) {
      throw createUsageError(`Unknown flag: ${arg}`);
    }
    if (shouldSkipValueToken(arg, args[index + 1])) {
      index += 1;
    }
  }

  return {
    command: commandOverride ?? "install",
    mode,
    withConfig,
    noPrompt,
    noInteractive,
    quiet,
    outputFormat,
    transport,
    skillsMode,
    fullInstall,
    rawArgs: args
  };
}

export function detectOutputFormat(argv: string[]): OutputFormat {
  const args = expandShortFlags(argv.slice(2));
  try {
    return parseOutputFormat(args);
  } catch {
    return "text";
  }
}

export function detectQuiet(argv: string[]): boolean {
  const args = expandShortFlags(argv.slice(2));
  return args.includes("--quiet");
}
