import { join, relative } from "node:path";
import { existsSync, mkdirSync, readdirSync, lstatSync, unlinkSync, symlinkSync } from "node:fs";
import { simpleGit } from "simple-git";
import type { Logger } from "pino";
import type { RepoConfig } from "@/types/index.js";

/**
 * Extracts the repository name from a Git SSH or HTTPS URL.
 * e.g. "git@github.com:HumandDev/web-cursor-rules.git" → "web-cursor-rules"
 */
function repoNameFromUrl(url: string): string {
  const last = url.split("/").pop() ?? url.split(":").pop() ?? url;
  return last.replace(/\.git$/, "");
}

/**
 * Clones (or pulls) each unique remote rules repo referenced by any RepoConfig,
 * then symlinks all `.mdc` files from its `rules/` directory into the
 * `.cursor/rules/` directory of every code repo that references it.
 */
export async function syncRemoteRulesRepos(
  repos: readonly RepoConfig[],
  workdirPath: string,
  logger: Logger,
): Promise<void> {
  const reposByUrl = new Map<string, string[]>();

  for (const repo of repos) {
    if (!repo.remoteRulesRepo) continue;
    const list = reposByUrl.get(repo.remoteRulesRepo) ?? [];
    list.push(repo.name);
    reposByUrl.set(repo.remoteRulesRepo, list);
  }

  if (reposByUrl.size === 0) return;

  for (const [rulesUrl, codeRepoNames] of reposByUrl) {
    const rulesRepoName = repoNameFromUrl(rulesUrl);
    const rulesRepoPath = join(workdirPath, rulesRepoName);
    const rulesDir = join(rulesRepoPath, "rules");

    try {
      if (existsSync(join(rulesRepoPath, ".git"))) {
        const git = simpleGit(rulesRepoPath);
        await git.pull();
        logger.info({ repo: rulesRepoName }, "rules_repo_pulled");
      } else {
        mkdirSync(rulesRepoPath, { recursive: true });
        await simpleGit().clone(rulesUrl, rulesRepoPath);
        logger.info({ repo: rulesRepoName, path: rulesRepoPath }, "rules_repo_cloned");
      }
    } catch (err) {
      logger.warn(
        { repo: rulesRepoName, error: err instanceof Error ? err.message : String(err) },
        "rules_repo_sync_failed",
      );
      continue;
    }

    if (!existsSync(rulesDir)) {
      logger.warn({ repo: rulesRepoName, path: rulesDir }, "rules_dir_not_found");
      continue;
    }

    const mdcFiles = readdirSync(rulesDir).filter((f) => f.endsWith(".mdc"));
    if (mdcFiles.length === 0) {
      logger.warn({ repo: rulesRepoName }, "no_mdc_files_found");
      continue;
    }

    for (const codeRepoName of codeRepoNames) {
      const targetDir = join(workdirPath, codeRepoName, ".cursor", "rules");
      mkdirSync(targetDir, { recursive: true });

      for (const file of mdcFiles) {
        const source = join(rulesDir, file);
        const target = join(targetDir, file);

        try {
          if (existsSync(target) && lstatSync(target).isSymbolicLink()) {
            unlinkSync(target);
          }
          symlinkSync(relative(targetDir, source), target);
        } catch (err) {
          logger.debug(
            { file, target, error: err instanceof Error ? err.message : String(err) },
            "symlink_failed",
          );
        }
      }

      logger.info(
        { rulesRepo: rulesRepoName, codeRepo: codeRepoName, files: mdcFiles.length },
        "rules_symlinked",
      );
    }
  }
}
