import { resolve, dirname, relative, sep } from "node:path";

const MODULES_DIR = "src/modules";

const rule = {
  meta: {
    type: "problem",
    docs: {
      description:
        "Disallow deep imports across module boundaries. Cross-module imports must go through index.ts.",
    },
    messages: {
      deepImport:
        "Cross-module imports are restricted to each module's index.ts. Import from '{{module}}' instead of '{{source}}'.",
    },
    schema: [
      {
        type: "object",
        properties: {
          allowTypeImports: {
            type: "array",
            items: { type: "string" },
            description:
              "Patterns for which type-only imports (`import type`) are allowed across module boundaries. " +
              "Each entry is matched against the resolved path relative to the modules root (e.g. " +
              '"**/generated/kysely-tailordb" matches any module\'s generated DB types, ' +
              '"finance-ledger" matches the module name).',
          },
        },
        additionalProperties: false,
      },
    ],
  },

  create(context) {
    const filename = context.filename ?? context.getFilename();
    const options = context.options[0] ?? {};
    const allowTypeImports = options.allowTypeImports ?? [];

    // Only check files inside src/modules/
    const relFile = relative(context.cwd ?? process.cwd(), filename)
      .split(sep)
      .join("/");
    if (!relFile.startsWith(MODULES_DIR + "/")) return {};

    // Extract current module name  (src/modules/<name>/...)
    const parts = relFile.slice((MODULES_DIR + "/").length).split("/");
    const currentModule = parts[0];

    return {
      ImportDeclaration(node) {
        check(context, node, currentModule, filename, allowTypeImports);
      },
      ExportNamedDeclaration(node) {
        if (node.source) check(context, node, currentModule, filename, allowTypeImports);
      },
    };
  },
};

/**
 * Match a pattern against a resolved module-relative path.
 *   - "finance-ledger"                → matches module name (first segment)
 *   - "** /generated/kysely-tailordb" → matches suffix after stripping leading "** /"
 */
function matchesPattern(pattern, targetModule, targetSubPath) {
  if (pattern.startsWith("**/")) {
    const suffix = pattern.slice(3);
    return targetSubPath === suffix || targetSubPath.endsWith("/" + suffix);
  }
  return pattern === targetModule;
}

function check(context, node, currentModule, filename, allowTypeImports) {
  const source = node.source?.value;
  if (!source || !source.startsWith(".")) return;

  // Resolve the import to an absolute path, then make it relative to cwd
  const resolved = resolve(dirname(filename), source).split(sep).join("/");
  const cwd = (context.cwd ?? process.cwd()).split(sep).join("/");
  const relResolved = relative(cwd, resolved).split(sep).join("/");

  // Check if the resolved path targets a different module
  if (!relResolved.startsWith(MODULES_DIR + "/")) return;
  const targetParts = relResolved.slice((MODULES_DIR + "/").length).split("/");
  const targetModule = targetParts[0];

  // Same module — always allowed
  if (targetModule === currentModule) return;

  // Cross-module: only index.ts (barrel) is allowed
  // targetParts would be [moduleName] for "import from '../other'" (resolves to index)
  // or [moduleName, "index"] / [moduleName, "index.ts"]
  if (targetParts.length === 1) return; // bare module dir → resolves to index
  if (targetParts.length === 2 && targetParts[1].startsWith("index")) return;

  // Type-only imports: check against allowTypeImports patterns
  if (node.importKind === "type" && allowTypeImports.length > 0) {
    const targetSubPath = targetParts.join("/"); // e.g. "finance-ledger/generated/kysely-tailordb"
    if (allowTypeImports.some((p) => matchesPattern(p, targetModule, targetSubPath))) return;
  }

  context.report({
    node: node.source,
    messageId: "deepImport",
    data: { module: targetModule, source },
  });
}

const plugin = {
  rules: {
    "no-cross-module-import": rule,
  },
};

export default plugin;
