import { join } from "node:path";

import * as smolToml from "smol-toml";
import { z } from "zod/mini";

import { RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH } from "../../constants/rulesync-paths.js";
import { AiFileParams, ValidationResult } from "../../types/ai-file.js";
import { readFileContent } from "../../utils/file.js";
import { RulesyncSubagent, RulesyncSubagentFrontmatter } from "./rulesync-subagent.js";
import {
  ToolSubagent,
  ToolSubagentForDeletionParams,
  ToolSubagentFromFileParams,
  ToolSubagentFromRulesyncSubagentParams,
  ToolSubagentSettablePaths,
} from "./tool-subagent.js";

export const CodexCliSubagentTomlSchema = z.looseObject({
  name: z.string(),
  description: z.optional(z.string()),
  developer_instructions: z.optional(z.string()),
  model: z.optional(z.string()),
  model_reasoning_effort: z.optional(z.string()),
  sandbox_mode: z.optional(z.string()),
});

type CodexCliSubagentToml = z.infer<typeof CodexCliSubagentTomlSchema>;

function stringifyCodexCliSubagentToml(tomlObj: CodexCliSubagentToml): string {
  const { developer_instructions, ...restFields } = tomlObj;
  const restToml = smolToml.stringify(restFields).trimEnd();

  if (developer_instructions === undefined) {
    return restToml;
  }

  const developerInstructionsToml = developer_instructions.includes("\n")
    ? developer_instructions.includes("'''")
      ? smolToml.stringify({ developer_instructions }).trimEnd()
      : `developer_instructions = '''\n${developer_instructions}\n'''`
    : smolToml.stringify({ developer_instructions }).trimEnd();

  return [restToml, developerInstructionsToml].filter((value) => value.length > 0).join("\n");
}

export type CodexCliSubagentParams = {
  body: string;
} & AiFileParams;

export class CodexCliSubagent extends ToolSubagent {
  private readonly body: string;

  constructor({ body, ...rest }: CodexCliSubagentParams) {
    if (rest.validate !== false) {
      try {
        const parsed = smolToml.parse(body);
        CodexCliSubagentTomlSchema.parse(parsed);
      } catch (error) {
        throw new Error(
          `Invalid TOML in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${
            error instanceof Error ? error.message : String(error)
          }`,
          { cause: error },
        );
      }
    }

    super({
      ...rest,
    });

    this.body = body;
  }

  static getSettablePaths(_options: { global?: boolean } = {}): ToolSubagentSettablePaths {
    return {
      relativeDirPath: join(".codex", "agents"),
    };
  }

  getBody(): string {
    return this.body;
  }

  toRulesyncSubagent(): RulesyncSubagent {
    let parsed: CodexCliSubagentToml;
    try {
      parsed = CodexCliSubagentTomlSchema.parse(smolToml.parse(this.body));
    } catch (error) {
      throw new Error(
        `Failed to parse TOML in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${
          error instanceof Error ? error.message : String(error)
        }`,
        { cause: error },
      );
    }
    const { name, description, developer_instructions, ...restFields } = parsed;

    // Build codexcli section with all fields except name, description, and developer_instructions
    const codexcliSection: Record<string, unknown> = {
      ...restFields,
    };

    const rulesyncFrontmatter: RulesyncSubagentFrontmatter = {
      targets: ["codexcli"],
      name,
      description: description,
      // Only include codexcli section if there are fields
      ...(Object.keys(codexcliSection).length > 0 && { codexcli: codexcliSection }),
    };

    return new RulesyncSubagent({
      outputRoot: ".",
      frontmatter: rulesyncFrontmatter,
      body: developer_instructions ?? "",
      relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
      relativeFilePath: this.getRelativeFilePath().replace(/\.toml$/, ".md"),
      validate: true,
    });
  }

  static fromRulesyncSubagent({
    outputRoot = process.cwd(),
    rulesyncSubagent,
    validate = true,
    global = false,
  }: ToolSubagentFromRulesyncSubagentParams): ToolSubagent {
    const frontmatter = rulesyncSubagent.getFrontmatter();
    const rawSection: Record<string, unknown> = frontmatter.codexcli ?? {};
    const codexcliSection = this.filterToolSpecificSection(rawSection, [
      "name",
      "description",
      "developer_instructions",
    ]);

    // Build TOML object from rulesync frontmatter + codexcli section (tool-specific fields only)
    const tomlObj: CodexCliSubagentToml = {
      name: frontmatter.name,
      ...(frontmatter.description ? { description: frontmatter.description } : {}),
      ...(rulesyncSubagent.getBody() ? { developer_instructions: rulesyncSubagent.getBody() } : {}),
      ...codexcliSection,
    };

    const body = stringifyCodexCliSubagentToml(tomlObj);
    const paths = this.getSettablePaths({ global });
    const relativeFilePath = rulesyncSubagent.getRelativeFilePath().replace(/\.md$/, ".toml");

    return new CodexCliSubagent({
      outputRoot,
      body,
      relativeDirPath: paths.relativeDirPath,
      relativeFilePath,
      fileContent: body,
      validate,
      global,
    });
  }

  validate(): ValidationResult {
    try {
      const parsed = smolToml.parse(this.body);
      CodexCliSubagentTomlSchema.parse(parsed);
      return { success: true, error: null };
    } catch (error) {
      return {
        success: false,
        error: error instanceof Error ? error : new Error(String(error)),
      };
    }
  }

  static isTargetedByRulesyncSubagent(rulesyncSubagent: RulesyncSubagent): boolean {
    return this.isTargetedByRulesyncSubagentDefault({
      rulesyncSubagent,
      toolTarget: "codexcli",
    });
  }

  static async fromFile({
    outputRoot = process.cwd(),
    relativeFilePath,
    validate = true,
    global = false,
  }: ToolSubagentFromFileParams): Promise<CodexCliSubagent> {
    const paths = this.getSettablePaths({ global });
    const filePath = join(outputRoot, paths.relativeDirPath, relativeFilePath);
    const fileContent = await readFileContent(filePath);

    const subagent = new CodexCliSubagent({
      outputRoot,
      relativeDirPath: paths.relativeDirPath,
      relativeFilePath,
      body: fileContent.trim(),
      fileContent,
      validate,
      global,
    });

    if (validate) {
      const result = subagent.validate();
      if (!result.success) {
        throw new Error(
          `Invalid TOML in ${filePath}: ${result.error instanceof Error ? result.error.message : String(result.error)}`,
        );
      }
    }

    return subagent;
  }

  static forDeletion({
    outputRoot = process.cwd(),
    relativeDirPath,
    relativeFilePath,
  }: ToolSubagentForDeletionParams): CodexCliSubagent {
    return new CodexCliSubagent({
      outputRoot,
      relativeDirPath,
      relativeFilePath,
      body: "",
      fileContent: "",
      validate: false,
    });
  }
}
