import path from "node:path";
import { z } from "zod";
import { defineCommand, arg } from "politty";
import { runCheck } from "../check";
import { runSyncCheck, formatSyncCheckReport } from "../sync-check";
import { runGenerateDoc, APP_DOC_TYPES, type DocType } from "../generate-doc";
import { runGenerateAppCode } from "../../generator/generate-app-code";
import { runGenerateSeed } from "../../generator/generate-seed";
import { runInitAppWithReadme } from "../init-module";
import { executeCommand, success, silentFailure } from "../lib/command-result";
import { progressCommand } from "./progress/index";

const cwd = process.cwd();

const pathArgs = z.object({
  path: arg(z.string(), {
    alias: "p",
    description: "Path to app directory",
  }),
});

const checkCommand = defineCommand({
  name: "check",
  description: "Validate app docs against schemas",
  args: pathArgs,
  run: (args) => {
    return executeCommand(async () => {
      const exitCode = await runCheck({ appRoot: args.path }, cwd);
      return exitCode === 0 ? success() : silentFailure(exitCode);
    });
  },
});

const syncCheckCommand = defineCommand({
  name: "sync-check",
  description: "Validate source <-> doc correspondence",
  args: pathArgs,
  run: (args) => {
    return executeCommand(async () => {
      const result = await runSyncCheck({ appRoot: args.path }, cwd);
      console.log(formatSyncCheckReport(result));
      return result.exitCode === 0 ? success() : silentFailure(result.exitCode);
    });
  },
});

const initCommand = defineCommand({
  name: "init",
  description: "Bootstrap a new app with directory structure and README",
  args: z.object({
    name: arg(z.string(), {
      positional: true,
      description: "App name",
    }),
    dir: arg(z.string(), {
      positional: true,
      description: "Parent directory where the app will be created",
    }),
  }),
  run: (args) => {
    return executeCommand(async () => {
      const exitCode = await runInitAppWithReadme(args.name, args.dir, cwd);
      return exitCode === 0 ? success() : silentFailure(exitCode);
    });
  },
});

const docCommand = defineCommand({
  name: "doc",
  description: "Create a documentation file from a schema template",
  args: pathArgs.extend({
    type: arg(z.enum(APP_DOC_TYPES as unknown as [string, ...string[]]), {
      positional: true,
      description: `Doc type (${APP_DOC_TYPES.join(", ")})`,
    }),
    name: arg(z.string().optional(), {
      positional: true,
      description: "Item name (required for most types)",
    }),
  }),
  run: (args) => {
    return executeCommand(async () => {
      const exitCode = await runGenerateDoc(args.type as DocType, args.name, args.path, cwd);
      return exitCode === 0 ? success() : silentFailure(exitCode);
    });
  },
});

const codeCommand = defineCommand({
  name: "code",
  description: "Generate boilerplate source files from docs",
  args: pathArgs,
  run: (args) => {
    return executeCommand(() => {
      const appPath = path.resolve(cwd, args.path);
      const exitCode = runGenerateAppCode(appPath);
      return exitCode === 0 ? success() : silentFailure(exitCode);
    });
  },
});

const seedCommand = defineCommand({
  name: "seed",
  description: "Generate seed data from module defaults",
  args: pathArgs,
  run: (args) => {
    return executeCommand(() => {
      const appPath = path.resolve(cwd, args.path);
      const exitCode = runGenerateSeed(appPath);
      return exitCode === 0 ? success() : silentFailure(exitCode);
    });
  },
});

const generateCommand = defineCommand({
  name: "generate",
  description: "Generate docs, code, and seed data for an app",
  subCommands: {
    doc: docCommand,
    code: codeCommand,
    seed: seedCommand,
  },
});

export const appCommand = defineCommand({
  name: "app",
  description: "App management",
  subCommands: {
    check: checkCommand,
    "sync-check": syncCheckCommand,
    init: initCommand,
    generate: generateCommand,
    progress: progressCommand,
  },
});
