import path from "node:path";
import fs from "node:fs";
import type { Database as DBType } from "better-sqlite3";

interface ScanStats {
  modules: number;
  symbols: number;
}

/** 递归收集目录下匹配扩展名的所有文件 */
function walkDir(dir: string, exts: string[]): string[] {
  if (!fs.existsSync(dir)) return [];
  const results: string[] = [];
  const stack: string[] = [dir];
  while (stack.length > 0) {
    const current = stack.pop()!;
    let entries: fs.Dirent[];
    try {
      entries = fs.readdirSync(current, { withFileTypes: true });
    } catch {
      continue;
    }
    for (const entry of entries) {
      const fullPath = path.join(current, entry.name);
      if (entry.isDirectory()) {
        if (["node_modules", "dist", ".git", "vendor"].includes(entry.name))
          continue;
        stack.push(fullPath);
      } else if (entry.isFile()) {
        const ext = path.extname(entry.name);
        if (exts.includes(ext)) results.push(fullPath);
      }
    }
  }
  return results;
}

// Go 符号正则：匹配导出函数、类型、结构体、接口
const GO_FUNC_RE = /^func\s+(?:\([^)]*\)\s+)?([A-Z]\w*)\s*\(/gm;
const GO_TYPE_RE = /^type\s+([A-Z]\w*)\s+/gm;
const GO_STRUCT_RE = /^type\s+([A-Z]\w*)\s+struct\s*\{/gm;
const GO_INTERFACE_RE = /^type\s+([A-Z]\w*)\s+interface\s*\{/gm;

export function scanGo(db: DBType, root: string): ScanStats {
  const insertModule = db.prepare(
    "INSERT OR IGNORE INTO modules (name, path, language, service, type) VALUES (?, ?, ?, ?, ?)",
  );
  const insertSymbol = db.prepare(
    "INSERT INTO symbols (module_id, name, kind, language, file_path, line_start, line_end, is_exported) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
  );
  const getModuleId = db.prepare("SELECT id FROM modules WHERE name = ?") as {
    get: (name: string) => { id: number } | undefined;
  };

  let modules = 0;
  let symbols = 0;

  // 扫描 services/* 下有 go.mod 的服务
  const servicesDir = path.join(root, "services");
  if (!fs.existsSync(servicesDir)) return { modules, symbols };

  const entries = fs.readdirSync(servicesDir, { withFileTypes: true });
  for (const entry of entries) {
    if (!entry.isDirectory()) continue;
    const serviceName = entry.name;
    const servicePath = path.join(servicesDir, serviceName);
    const hasGoMod = fs.existsSync(path.join(servicePath, "go.mod"));
    if (!hasGoMod) continue;

    insertModule.run(serviceName, servicePath, "go", serviceName, "service");
    const modRow = getModuleId.get(serviceName);
    if (modRow) {
      modules++;
      // 扫描 .go 文件
      const goFiles = walkDir(servicePath, [".go"]);
      for (const filePath of goFiles) {
        // 跳过测试文件
        if (filePath.endsWith("_test.go")) continue;
        let content: string;
        try {
          content = fs.readFileSync(filePath, "utf-8");
        } catch {
          continue;
        }
        const lines = content.split("\n");

        // 函数
        let match: RegExpExecArray | null;
        const funcRe = new RegExp(GO_FUNC_RE);
        while ((match = funcRe.exec(content)) !== null) {
          const name = match[1];
          const lineNum = content.slice(0, match.index).split("\n").length;
          insertSymbol.run(
            modRow.id,
            name,
            "function",
            "go",
            filePath,
            lineNum,
            lineNum,
            1,
          );
          symbols++;
        }

        // 类型（struct / interface / type alias）
        const typeRe = new RegExp(GO_TYPE_RE);
        while ((match = typeRe.exec(content)) !== null) {
          const name = match[1];
          const lineNum = content.slice(0, match.index).split("\n").length;
          // 判断是 struct 还是 interface 还是普通 type
          const line = lines[lineNum - 1] || "";
          let kind = "type";
          if (line.includes("struct")) kind = "struct";
          else if (line.includes("interface")) kind = "interface";
          insertSymbol.run(
            modRow.id,
            name,
            kind,
            "go",
            filePath,
            lineNum,
            lineNum,
            1,
          );
          symbols++;
        }
      }
    }
  }

  return { modules, symbols };
}
