#!/usr/bin/env bun
import fs from "fs";
import path from "path";
import * as parser from "@babel/parser";
import traverse from "@babel/traverse";
import * as t from "@babel/types";

import { readdirSync, statSync, writeFileSync } from "fs";
import _ from "lodash";
import { basename, extname, join, relative } from "path";

const PAGES_DIR = join(process.cwd(), "src/pages");
const OUTPUT_FILE = join(process.cwd(), "src/AppRoutes.tsx");

/**
 * ✅ Ubah nama file menjadi PascalCase
 * - Support: snake_case, kebab-case, camelCase, PascalCase
 */
const toComponentName = (fileName: string): string => {
  return fileName
    .replace(/\.[^/.]+$/, "") // hilangkan ekstensi file
    .replace(/[_-]+/g, " ")   // snake_case & kebab-case → spasi
    .replace(/([a-z])([A-Z])/g, "$1 $2") // camelCase → spasi
    .replace(/\b\w/g, (c) => c.toUpperCase()) // kapital tiap kata
    .replace(/\s+/g, ""); // gabung semua → PascalCase
};

/**
 * ✅ Normalisasi nama menjadi path route (kebab-case)
 */
function toRoutePath(name: string): string {
  name = name.replace(/\.[^/.]+$/, ""); // hapus ekstensi

  if (name.toLowerCase() === "home") return "/";
  if (name.toLowerCase() === "login") return "/login";
  if (name.toLowerCase() === "notfound") return "/*";

  // Hapus prefix/suffix umum
  name = name.replace(/_page$/i, "").replace(/^form_/i, "");

  // ✅ Normalisasi ke kebab-case
  return _.kebabCase(name);
}

// 🧭 Scan folder pages secara rekursif
function scan(dir: string): any[] {
  const items = readdirSync(dir);
  const routes: any[] = [];

  for (const item of items) {
    const full = join(dir, item);
    const stat = statSync(full);

    if (stat.isDirectory()) {
      routes.push({
        name: item,
        path: _.kebabCase(item),
        children: scan(full),
      });
    } else if (extname(item) === ".tsx") {
      routes.push({
        name: basename(item, ".tsx"),
        filePath: relative(join(process.cwd(), "src"), full).replace(/\\/g, "/"),
      });
    }
  }
  return routes;
}

// 🏗️ Generate <Route> JSX dari struktur folder
function generateJSX(routes: any[], parentPath = ""): string {
  let jsx = "";

  for (const route of routes) {
    if (route.children) {
      const layout = route.children.find((r: any) => r.name.endsWith("_layout"));

      if (layout) {
        const LayoutComponent = toComponentName(layout.name.replace("_layout", "Layout"));
        const nested = route.children.filter((r: any) => r !== layout);
        const nestedRoutes = generateJSX(nested, `${parentPath}/${route.path}`);

        const homeFile = route.children.find((r: any) =>
          r.name.toLowerCase().endsWith("_home")
        );
        const indexRoute = homeFile
          ? `<Route index element={<${toComponentName(homeFile.name)} />} />\n`
          : "";

        jsx += `
          <Route path="${parentPath}/${route.path}" element={<${LayoutComponent} />}>
            ${indexRoute}
            ${nestedRoutes}
          </Route>
        `;
      } else {
        jsx += generateJSX(route.children, `${parentPath}/${route.path}`);
      }
    } else {
      const Component = toComponentName(route.name);
      const routePath = toRoutePath(route.name);

      const fullPath = routePath.startsWith("/")
        ? routePath
        : `${parentPath}/${routePath}`.replace(/\/+/g, "/");

      jsx += `<Route path="${fullPath}" element={<${Component} />} />\n`;
    }
  }
  return jsx;
}

// 🧾 Generate import otomatis
function generateImports(routes: any[]): string {
  const imports = new Set<string>();

  function collect(rs: any[]) {
    for (const r of rs) {
      if (r.children) collect(r.children);
      else {
        const Comp = toComponentName(r.name);
        imports.add(`import ${Comp} from "./${r.filePath.replace(/\.tsx$/, "")}";`);
      }
    }
  }
  collect(routes);
  return Array.from(imports).join("\n");
}

function generateRoutes() {
  const allRoutes = scan(PAGES_DIR);
  const imports = generateImports(allRoutes);
  const jsxRoutes = generateJSX(allRoutes);

  const finalCode = `
// ⚡ Auto-generated by generateRoutes.ts — DO NOT EDIT MANUALLY
import { BrowserRouter, Routes, Route } from "react-router-dom";
${imports}

export default function AppRoutes() {
	return (
		<BrowserRouter>
			<Routes>
				${jsxRoutes}
			</Routes>
		</BrowserRouter>
	);
}
`;

  writeFileSync(OUTPUT_FILE, finalCode);
  console.log(`✅ Routes generated → ${OUTPUT_FILE}`);
  Bun.spawnSync(["bunx", "prettier", "--write", "src/**/*.tsx"]);
}

// --- Extract untuk clientRoutes.ts ---
const SRC_DIR = path.resolve(process.cwd(), "src");
const APP_ROUTES_FILE = path.join(SRC_DIR, "AppRoutes.tsx");

interface RouteNode {
  path: string;
  children: RouteNode[];
}

function getAttributePath(attrs: (t.JSXAttribute | t.JSXSpreadAttribute)[]) {
  const pathAttr = attrs.find(
    (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: "path" })
  ) as t.JSXAttribute | undefined;

  if (pathAttr && t.isStringLiteral(pathAttr.value)) return pathAttr.value.value;
  return "";
}

function extractRouteNodes(node: t.JSXElement): RouteNode | null {
  const opening = node.openingElement;
  if (!t.isJSXIdentifier(opening.name) || opening.name.name !== "Route") return null;

  const currentPath = getAttributePath(opening.attributes);
  const children: RouteNode[] = [];

  for (const child of node.children) {
    if (t.isJSXElement(child)) {
      const childNode = extractRouteNodes(child);
      if (childNode) children.push(childNode);
    }
  }
  return { path: currentPath, children };
}

function flattenRoutes(node: RouteNode, parentPath = ""): Record<string, string> {
  const record: Record<string, string> = {};
  let fullPath = node.path;

  if (fullPath) {
    if (!fullPath.startsWith("/")) {
      if (parentPath) {
        if (fullPath === "/") fullPath = parentPath;
        else fullPath = `${parentPath.replace(/\/$/, "")}/${fullPath}`;
      }
      if (!fullPath.startsWith("/")) fullPath = `/${fullPath}`;
    }
    fullPath = fullPath.replace(/\/+/g, "/");
    record[fullPath] = fullPath;
  }

  for (const child of node.children) {
    Object.assign(record, flattenRoutes(child, fullPath || parentPath));
  }
  return record;
}

function extractRoutes(code: string): Record<string, string> {
  const ast = parser.parse(code, {
    sourceType: "module",
    plugins: ["typescript", "jsx"],
  });

  const routes: Record<string, string> = {};
  traverse(ast, {
    JSXElement(path) {
      const opening = path.node.openingElement;
      if (t.isJSXIdentifier(opening.name) && opening.name.name === "Routes") {
        for (const child of path.node.children) {
          if (t.isJSXElement(child)) {
            const node = extractRouteNodes(child);
            if (node) Object.assign(routes, flattenRoutes(node));
          }
        }
      }
    },
  });

  return routes;
}

export default function route() {
  generateRoutes();

  if (!fs.existsSync(APP_ROUTES_FILE)) {
    console.error("❌ AppRoutes.tsx not found in src/");
    process.exit(1);
  }

  const code = fs.readFileSync(APP_ROUTES_FILE, "utf-8");
  const routes = extractRoutes(code);

  console.log("✅ Generated Routes:");
  console.log(routes);

  const outPath = path.join(SRC_DIR, "clientRoutes.ts");
  fs.writeFileSync(
    outPath,
    `// AUTO-GENERATED FILE\nconst clientRoutes = ${JSON.stringify(routes, null, 2)} as const;\n\nexport default clientRoutes;`
  );

  console.log(`📄 clientRoutes.ts saved → ${outPath}`);
}

route()

