#!/usr/bin/env node
/**
 * One-off script to generate REST route files for the 37 resources that have operations but no routes.
 * Run from repo root: node packages/@openhi/constructs/scripts/generate-routes.js
 */

const fs = require("fs");
const path = require("path");

const RESOURCES = [
  { key: "capabilitystatement", type: "CapabilityStatement" },
  { key: "codesystem", type: "CodeSystem" },
  { key: "compartmentdefinition", type: "CompartmentDefinition" },
  { key: "conceptmap", type: "ConceptMap" },
  { key: "examplescenario", type: "ExampleScenario" },
  { key: "graphdefinition", type: "GraphDefinition" },
  { key: "implementationguide", type: "ImplementationGuide" },
  { key: "medicinalproduct", type: "MedicinalProduct" },
  { key: "medicinalproductauthorization", type: "MedicinalProductAuthorization" },
  { key: "medicinalproductcontraindication", type: "MedicinalProductContraindication" },
  { key: "medicinalproductingredient", type: "MedicinalProductIngredient" },
  { key: "medicinalproductindication", type: "MedicinalProductIndication" },
  { key: "medicinalproductinteraction", type: "MedicinalProductInteraction" },
  { key: "medicinalproductmanufactured", type: "MedicinalProductManufactured" },
  { key: "medicinalproductpackaged", type: "MedicinalProductPackaged" },
  { key: "medicinalproductpharmaceutical", type: "MedicinalProductPharmaceutical" },
  { key: "medicinalproductundesirableeffect", type: "MedicinalProductUndesirableEffect" },
  { key: "messagedefinition", type: "MessageDefinition" },
  { key: "namingsystem", type: "NamingSystem" },
  { key: "observationdefinition", type: "ObservationDefinition" },
  { key: "operationdefinition", type: "OperationDefinition" },
  { key: "researchdefinition", type: "ResearchDefinition" },
  { key: "researchelementdefinition", type: "ResearchElementDefinition" },
  { key: "searchparameter", type: "SearchParameter" },
  { key: "specimendefinition", type: "SpecimenDefinition" },
  { key: "structuredefinition", type: "StructureDefinition" },
  { key: "structuremap", type: "StructureMap" },
  { key: "substancenucleicacid", type: "SubstanceNucleicAcid" },
  { key: "substancepolymer", type: "SubstancePolymer" },
  { key: "substanceprotein", type: "SubstanceProtein" },
  { key: "substancereferenceinformation", type: "SubstanceReferenceInformation" },
  { key: "substancespecification", type: "SubstanceSpecification" },
  { key: "substancesourcematerial", type: "SubstanceSourceMaterial" },
  { key: "terminologycapabilities", type: "TerminologyCapabilities" },
  { key: "testreport", type: "TestReport" },
  { key: "testscript", type: "TestScript" },
  { key: "valueset", type: "ValueSet" },
];

function toBasePathKey(key) {
  return key.toUpperCase().replace(/-/g, "");
}

function createRouteName(type, suffix) {
  const base = type.charAt(0).toLowerCase() + type.slice(1);
  if (suffix === "List") return "list" + type + "s";
  if (suffix === "Create") return "create" + type;
  if (suffix === "GetById") return "get" + type + "ById";
  if (suffix === "Update") return "update" + type;
  if (suffix === "Delete") return "delete" + type;
  return base;
}

function writeFile(filePath, content) {
  const dir = path.dirname(filePath);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
  fs.writeFileSync(filePath, content, "utf8");
}

const ROUTES_DIR = path.join(__dirname, "..", "src", "data", "rest-api", "routes", "data");

RESOURCES.forEach(({ key, type }) => {
  const basePathKey = toBasePathKey(key);
  const createRoute = "create" + type + "Route";
  const listRoute = "list" + type + "sRoute";
  const getByIdRoute = "get" + type + "ByIdRoute";
  const updateRoute = "update" + type + "Route";
  const deleteRoute = "delete" + type + "Route";
  const routerName = key + "Router";

  const dir = path.join(ROUTES_DIR, key);

  writeFile(
    path.join(dir, `${key}-create-route.ts`),
    `import type { ${type} } from "@openhi/types";
import type { Request, Response } from "express";
import { create${type}Operation } from "../../../../operations/data/${key}/${key}-create-operation";
import {
  BASE_PATH,
  requireJsonBodyAs,
  sendOperationOutcome500,
} from "../../common";

/** POST /${type} — create: accepts ${type} in body, persists via data layer, returns 201. */
export async function ${createRoute}(
  req: Request,
  res: Response,
): Promise<Response> {
  const bodyResult = requireJsonBodyAs<${type}>(req, res);
  if ("errorResponse" in bodyResult) return bodyResult.errorResponse;

  const ctx = req.openhiContext!;
  const body = bodyResult.body;
  const resource: ${type} = {
    ...body,
    resourceType: "${type}",
  } as ${type};

  try {
    const result = await create${type}Operation({
      context: ctx,
      body: resource,
    });
    return res
      .status(201)
      .location(\`\${BASE_PATH.${basePathKey}}/\${result.id}\`)
      .json(result.resource);
  } catch (err: unknown) {
    return sendOperationOutcome500(res, err, "POST ${type} error:");
  }
}
`
  );

  writeFile(
    path.join(dir, `${key}-list-route.ts`),
    `import type { Request, Response } from "express";
import { list${type}sOperation } from "../../../../operations/data/${key}/${key}-list-operation";
import {
  BASE_PATH,
  buildSearchsetBundle,
  sendOperationOutcome500,
} from "../../common";

/**
 * GET /${type} — search/list: returns a FHIR Bundle (searchset) from the data store.
 * Uses GSI1 (Unified Sharded List per ADR-011) to list all ${type}s in the workspace without a table scan.
 */
export async function ${listRoute}(
  req: Request,
  res: Response,
): Promise<Response> {
  const ctx = req.openhiContext!;

  try {
    const result = await list${type}sOperation({ context: ctx });
    const bundle = buildSearchsetBundle(BASE_PATH.${basePathKey}, result.entries);
    return res.json(bundle);
  } catch (err: unknown) {
    return sendOperationOutcome500(res, err, "GET /${type} list error:");
  }
}
`
  );

  writeFile(
    path.join(dir, `${key}-get-by-id-route.ts`),
    `import type { Request, Response } from "express";
import { NotFoundError, domainErrorToHttpStatus } from "../../../../errors";
import { get${type}ByIdOperation } from "../../../../operations/data/${key}/${key}-get-by-id-operation";
import { sendOperationOutcome404, sendOperationOutcome500 } from "../../common";

/** GET /${type}/:id — read: returns a single ${type} resource from the data store or 404. */
export async function ${getByIdRoute}(
  req: Request,
  res: Response,
): Promise<Response> {
  const id = String(req.params.id);
  const ctx = req.openhiContext!;

  try {
    const result = await get${type}ByIdOperation({ context: ctx, id });
    return res.json(result.resource);
  } catch (err: unknown) {
    const status = domainErrorToHttpStatus(err);
    if (status === 404) {
      const diagnostics =
        err instanceof NotFoundError ? err.message : \`${type} \${id} not found\`;
      return sendOperationOutcome404(res, diagnostics);
    }
    return sendOperationOutcome500(res, err, "GET ${type} error:");
  }
}
`
  );

  writeFile(
    path.join(dir, `${key}-update-route.ts`),
    `import type { ${type} } from "@openhi/types";
import type { Request, Response } from "express";
import { NotFoundError, domainErrorToHttpStatus } from "../../../../errors";
import { update${type}Operation } from "../../../../operations/data/${key}/${key}-update-operation";
import {
  requireJsonBodyAs,
  sendOperationOutcome404,
  sendOperationOutcome500,
} from "../../common";

/** PUT /${type}/:id — update: accepts ${type} in body, persists via data layer, returns 200. */
export async function ${updateRoute}(
  req: Request,
  res: Response,
): Promise<Response> {
  const bodyResult = requireJsonBodyAs<${type}>(req, res);
  if ("errorResponse" in bodyResult) return bodyResult.errorResponse;

  const id = String(req.params.id);
  const ctx = req.openhiContext!;
  const body = bodyResult.body;
  const resource: ${type} = {
    ...body,
    resourceType: "${type}",
    id,
  } as ${type};

  try {
    const result = await update${type}Operation({
      context: ctx,
      id,
      body: resource,
    });
    return res.json(result.resource);
  } catch (err: unknown) {
    const status = domainErrorToHttpStatus(err);
    if (status === 404) {
      const diagnostics =
        err instanceof NotFoundError ? err.message : \`${type} \${id} not found\`;
      return sendOperationOutcome404(res, diagnostics);
    }
    return sendOperationOutcome500(res, err, "PUT ${type} error:");
  }
}
`
  );

  writeFile(
    path.join(dir, `${key}-delete-route.ts`),
    `import type { Request, Response } from "express";
import { delete${type}Operation } from "../../../../operations/data/${key}/${key}-delete-operation";
import { sendOperationOutcome500 } from "../../common";

/** DELETE /${type}/:id — delete: removes from data store, returns 204. */
export async function ${deleteRoute}(
  req: Request,
  res: Response,
): Promise<Response> {
  const id = String(req.params.id);
  const ctx = req.openhiContext!;

  try {
    await delete${type}Operation({ context: ctx, id });
    return res.status(204).send();
  } catch (err: unknown) {
    return sendOperationOutcome500(res, err, "DELETE ${type} error:");
  }
}
`
  );

  writeFile(
    path.join(dir, `${key}.ts`),
    `import express from "express";
import { ${createRoute} } from "./${key}-create-route";
import { ${deleteRoute} } from "./${key}-delete-route";
import { ${getByIdRoute} } from "./${key}-get-by-id-route";
import { ${listRoute} } from "./${key}-list-route";
import { ${updateRoute} } from "./${key}-update-route";

/**
 * ${type} REST router: /${type}
 * FHIR R4 ${type} resource.
 */
const router: express.Router = express.Router();

router.get("/", ${listRoute});
router.get("/:id", ${getByIdRoute});
router.post("/", ${createRoute});
router.put("/:id", ${updateRoute});
router.delete("/:id", ${deleteRoute});

export { router as ${routerName} };
`
  );
});

console.log("Generated routes for", RESOURCES.length, "resources.");
