import fs from "node:fs";
import path from "node:path";
import { parseCommandDoc, type ParsedCommandDoc } from "./parse-command-doc";
import { generateErrors } from "./generate-errors";
import { generatePermissions } from "./generate-permissions";
import { generateCommandShell, generateQueryShell } from "./generate-shells";
import { renderStub } from "./stub-templates";
import { scaffoldModuleBoilerplate } from "./scaffold";

export function runGenerateCode(modulePath: string, moduleName: string): number {
  scaffoldModuleBoilerplate(modulePath, moduleName);

  const docsDir = path.join(modulePath, "docs", "commands");
  const libDir = path.join(modulePath, "lib");
  const commandDir = path.join(modulePath, "command");

  if (!fs.existsSync(docsDir)) {
    console.log(`No docs/commands/ directory found — skipping code generation`);
    console.log(`Generated boilerplate for ${moduleName}`);
    return 0;
  }

  const mdFiles = fs.readdirSync(docsDir).filter((f) => f.endsWith(".md"));
  if (mdFiles.length === 0) {
    console.log(`No command docs found — skipping code generation`);
    console.log(`Generated boilerplate for ${moduleName}`);
    return 0;
  }

  const parsedDocs: ParsedCommandDoc[] = [];

  for (const file of mdFiles) {
    const content = fs.readFileSync(path.join(docsDir, file), "utf-8");
    const name = path.basename(file, ".md");
    parsedDocs.push(parseCommandDoc(name, content));
  }

  // Also parse query docs for error extraction
  const queryDocsDir = path.join(modulePath, "docs", "queries");
  const allDocsForErrors = [...parsedDocs];
  if (fs.existsSync(queryDocsDir)) {
    const queryFiles = fs.readdirSync(queryDocsDir).filter((f) => f.endsWith(".md"));
    for (const file of queryFiles) {
      const content = fs.readFileSync(path.join(queryDocsDir, file), "utf-8");
      const name = path.basename(file, ".md");
      allDocsForErrors.push(parseCommandDoc(name, content));
    }
  }

  let generated = 0;

  const errorsContent = generateErrors(moduleName, allDocsForErrors);
  if (errorsContent) {
    fs.mkdirSync(libDir, { recursive: true });
    const errorsFile = path.join(libDir, "errors.generated.ts");
    fs.writeFileSync(errorsFile, errorsContent);
    console.log(`  wrote ${path.relative(modulePath, errorsFile)}`);
    generated++;
  }

  const commandNames = parsedDocs.map((d) => d.commandName);
  const permissionsContent = generatePermissions(moduleName, commandNames);
  const permissionsFile = path.join(libDir, "permissions.generated.ts");
  fs.writeFileSync(permissionsFile, permissionsContent);
  console.log(`  wrote ${path.relative(modulePath, permissionsFile)}`);
  generated++;

  fs.mkdirSync(commandDir, { recursive: true });
  for (const doc of parsedDocs) {
    const implFile = path.join(commandDir, `${doc.commandName}.ts`);
    const shellContent = generateCommandShell(doc);
    const shellFile = path.join(commandDir, `${doc.commandName}.generated.ts`);
    fs.writeFileSync(shellFile, shellContent);
    console.log(`  wrote ${path.relative(modulePath, shellFile)}`);
    generated++;

    if (!fs.existsSync(implFile)) {
      fs.writeFileSync(implFile, renderStub("command", doc.commandName));
      console.log(`  scaffolded ${path.relative(modulePath, implFile)}`);
    }

    const testFile = path.join(commandDir, `${doc.commandName}.test.ts`);
    if (!fs.existsSync(testFile)) {
      fs.writeFileSync(testFile, renderStub("commandTest", doc.commandName));
      console.log(`  scaffolded ${path.relative(modulePath, testFile)}`);
    }
  }

  // Generate query files from docs/queries/*.md
  if (fs.existsSync(queryDocsDir)) {
    const queryFiles = fs.readdirSync(queryDocsDir).filter((f) => f.endsWith(".md"));
    if (queryFiles.length > 0) {
      const queryDir = path.join(modulePath, "query");
      fs.mkdirSync(queryDir, { recursive: true });

      for (const file of queryFiles) {
        const content = fs.readFileSync(path.join(queryDocsDir, file), "utf-8");
        const name = path.basename(file, ".md");
        const doc = parseCommandDoc(name, content);

        const shellFile = path.join(queryDir, `${doc.commandName}.generated.ts`);
        fs.writeFileSync(shellFile, generateQueryShell(doc));
        console.log(`  wrote ${path.relative(modulePath, shellFile)}`);
        generated++;

        const implFile = path.join(queryDir, `${doc.commandName}.ts`);
        if (!fs.existsSync(implFile)) {
          fs.writeFileSync(implFile, renderStub("query", doc.commandName));
          console.log(`  scaffolded ${path.relative(modulePath, implFile)}`);
        }

        const testFile = path.join(queryDir, `${doc.commandName}.test.ts`);
        if (!fs.existsSync(testFile)) {
          fs.writeFileSync(testFile, renderStub("queryTest", doc.commandName));
          console.log(`  scaffolded ${path.relative(modulePath, testFile)}`);
        }
      }
    }
  }

  // Generate db stubs from docs/models/*.md
  const modelDocsDir = path.join(modulePath, "docs", "models");
  if (fs.existsSync(modelDocsDir)) {
    const modelFiles = fs.readdirSync(modelDocsDir).filter((f) => f.endsWith(".md"));
    if (modelFiles.length > 0) {
      const dbDir = path.join(modulePath, "db");
      fs.mkdirSync(dbDir, { recursive: true });

      for (const file of modelFiles) {
        const modelName = path.basename(file, ".md");
        const camelName = modelName.charAt(0).toLowerCase() + modelName.slice(1);
        const dbFile = path.join(dbDir, `${camelName}.ts`);
        if (!fs.existsSync(dbFile)) {
          fs.writeFileSync(dbFile, renderStub("db", camelName));
          console.log(`  scaffolded ${path.relative(modulePath, dbFile)}`);
        }
      }
    }
  }

  console.log(`Generated ${generated} file(s) for ${moduleName}`);
  return 0;
}
