import { XMLBuilder, XMLParser } from "fast-xml-parser";
import type { FastifyInstance } from "fastify";
import { z } from "zod";
import { InputValidationError } from "../../modality/contract.js";
import { createToolRoute } from "../tool-factory.js";

const settingsSchema = z.object({
  pretty: z.boolean().default(true),
});

export function registerJsonXml(app: FastifyInstance) {
  createToolRoute(app, {
    toolId: "json-xml",
    settingsSchema,
    process: async () => {
      throw new Error("json-xml is v2-only");
    },
    processV2: async (ctx) => {
      const settings = settingsSchema.parse(ctx.settings);
      const input = ctx.inputs[0];
      const base = input.filename.replace(/\.[^.]+$/, "");
      const lower = input.filename.toLowerCase();
      const text = input.buffer.toString("utf8");

      if (lower.endsWith(".xml")) {
        // xml -> json
        const parser = new XMLParser({ ignoreAttributes: false });
        const parsed = parser.parse(text);
        const json = JSON.stringify(parsed, null, settings.pretty ? 2 : 0);
        return {
          buffer: Buffer.from(json, "utf8"),
          filename: `${base}.json`,
          contentType: "application/json",
        };
      }

      // json -> xml
      let data: unknown;
      try {
        data = JSON.parse(text);
      } catch (err: unknown) {
        const msg = err instanceof Error ? err.message : String(err);
        throw new InputValidationError(`Not valid JSON: ${msg.split("\n")[0]}`);
      }
      // The builder needs an object/array root; primitives (null, string, number,
      // boolean) crash builder.build or produce per-character garbage XML.
      if (data === null || typeof data !== "object") {
        throw new InputValidationError("JSON must be an object or array to convert to XML");
      }
      // Wrap in a root element when the top level is an array or has multiple
      // keys, so the XML is well-formed with a single root.
      const wrapped = Array.isArray(data) || Object.keys(data).length !== 1 ? { root: data } : data;
      const builder = new XMLBuilder({ format: settings.pretty, ignoreAttributes: false });
      const xml = builder.build(wrapped) as string;
      return {
        buffer: Buffer.from(xml, "utf8"),
        filename: `${base}.xml`,
        contentType: "application/xml",
      };
    },
  });
}
