import { readdir, readFile, stat } from "node:fs/promises";
import path from "node:path";
import process from "node:process";
import { resolveRealWorkspaceRoot } from "./load-env.mjs";

const rootDir = resolveRealWorkspaceRoot();
const workspaceDirs = ["apps", "packages", "tooling"];
const sourceFileExtensions = new Set([
  ".ts",
  ".tsx",
  ".mts",
  ".cts",
  ".js",
  ".jsx",
  ".mjs",
  ".cjs",
]);
const resolutionExtensions = [
  "",
  ".ts",
  ".tsx",
  ".mts",
  ".cts",
  ".js",
  ".jsx",
  ".mjs",
  ".cjs",
];
const violations = [];

const importPattern =
  /(?:import|export)\s+(?:[^"'`]*?\s+from\s+)?["'`](\.{1,2}\/[^"'`]+)["'`]|import\s*\(\s*["'`](\.{1,2}\/[^"'`]+)["'`]\s*\)/g;

async function pathExists(targetPath) {
  try {
    await stat(targetPath);
    return true;
  } catch {
    return false;
  }
}

async function collectSourceFiles(directoryPath, result) {
  const entries = await readdir(directoryPath, { withFileTypes: true });

  for (const entry of entries) {
    const absolutePath = path.join(directoryPath, entry.name);

    if (entry.isDirectory()) {
      if (
        entry.name === "node_modules"
        || entry.name.startsWith(".next")
        || entry.name === ".turbo"
        || entry.name === "dist"
        || entry.name === "coverage"
        || entry.name === "playwright-report"
        || entry.name === "test-results"
      ) {
        continue;
      }
      await collectSourceFiles(absolutePath, result);
      continue;
    }

    if (!entry.isFile()) {
      continue;
    }

    const extension = path.extname(entry.name);
    if (sourceFileExtensions.has(extension)) {
      result.push(absolutePath);
    }
  }
}

function getExtensionlessCandidates(basePath) {
  return resolutionExtensions.flatMap((extension) => [
    `${basePath}${extension}`,
    path.join(basePath, `index${extension}`),
  ]);
}

function getExplicitExtensionCandidates(basePath, importExtension) {
  const baseWithoutExtension = basePath.slice(0, -importExtension.length);
  const siblingExtensions =
    importExtension === ".js" || importExtension === ".mjs" || importExtension === ".cjs"
      ? [importExtension, ".ts", ".tsx", ".mts", ".cts"]
      : [importExtension];

  return siblingExtensions.flatMap((extension) => [
    `${baseWithoutExtension}${extension}`,
    path.join(baseWithoutExtension, `index${extension}`),
  ]);
}

async function resolvesImport(fromFilePath, importPath) {
  const resolvedBasePath = path.resolve(path.dirname(fromFilePath), importPath);
  const rawExtension = path.extname(importPath);
  const importExtension = resolutionExtensions.includes(rawExtension) ? rawExtension : "";
  const candidates = importExtension
    ? getExplicitExtensionCandidates(resolvedBasePath, importExtension)
    : getExtensionlessCandidates(resolvedBasePath);

  for (const candidate of candidates) {
    if (await pathExists(candidate)) {
      return true;
    }
  }

  return false;
}

const sourceFiles = [];

for (const workspaceDir of workspaceDirs) {
  const absoluteWorkspaceDir = path.join(rootDir, workspaceDir);
  if (!(await pathExists(absoluteWorkspaceDir))) {
    continue;
  }
  await collectSourceFiles(absoluteWorkspaceDir, sourceFiles);
}

for (const sourceFile of sourceFiles) {
  const content = await readFile(sourceFile, "utf8");
  const relativeSourceFile = path.relative(rootDir, sourceFile);
  const imports = new Set();

  for (const match of content.matchAll(importPattern)) {
    const importPath = match[1] ?? match[2];
    if (importPath) {
      imports.add(importPath.trim());
    }
  }

  for (const importPath of imports) {
    if (!(await resolvesImport(sourceFile, importPath))) {
      violations.push(`${relativeSourceFile}: unresolved relative import ${importPath}`);
    }
  }
}

if (violations.length > 0) {
  console.error("Workspace import check failed:");
  for (const violation of violations) {
    console.error(`- ${violation}`);
  }
  process.exit(1);
}

console.log("Workspace imports passed.");
