import fs from "node:fs";
import path from "node:path";
import { type CommandResult, success } from "../../../lib/command-result";
import { MODULE_PATHS } from "../../../lib/paths";
import { generateErrors } from "./generate-errors";
import { generateLifecycle } from "./generate-lifecycle";
import { generatePermissions } from "./generate-permissions";
import { generateCommandShell, generateQueryShell } from "./generate-shells";
import { parseCommandDoc, type ParsedCommandDoc } from "./parse-command-doc";
import { parseModelDoc, type ParsedModelDoc } from "./parse-model-doc";
import {
  renderCommandStub,
  renderCommandTestStub,
  renderDbStub,
  renderQueryStub,
  renderQueryTestStub,
} from "./stubs";

export function runModuleGenerateCode(modulePath: string, moduleName: string): CommandResult {
  const libDir = path.join(modulePath, "lib");
  let generated = 0;

  // Parse command docs
  const commandDocsDir = path.join(modulePath, MODULE_PATHS.docs.command);
  const parsedCommandDocs: ParsedCommandDoc[] = [];
  if (fs.existsSync(commandDocsDir)) {
    const mdFiles = fs.readdirSync(commandDocsDir).filter((f) => f.endsWith(".md"));
    for (const file of mdFiles) {
      const content = fs.readFileSync(path.join(commandDocsDir, file), "utf-8");
      const name = path.basename(file, ".md");
      parsedCommandDocs.push(parseCommandDoc(name, content));
    }
  }

  // Parse query docs
  const queryDocsDir = path.join(modulePath, MODULE_PATHS.docs.query);
  const parsedQueryDocs: ParsedCommandDoc[] = [];
  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");
      parsedQueryDocs.push(parseCommandDoc(name, content));
    }
  }
  // Parse model docs
  const modelDocsDir = path.join(modulePath, MODULE_PATHS.docs.model);
  const parsedModelDocs: ParsedModelDoc[] = [];
  if (fs.existsSync(modelDocsDir)) {
    const modelFiles = fs.readdirSync(modelDocsDir).filter((f) => f.endsWith(".md"));
    for (const file of modelFiles) {
      const content = fs.readFileSync(path.join(modelDocsDir, file), "utf-8");
      const modelName = path.basename(file, ".md");
      parsedModelDocs.push(parseModelDoc(modelName, content));
    }
  }

  const allDocsForErrors = [...parsedCommandDocs, ...parsedQueryDocs];

  // Generate errors and permissions from command/query docs
  if (allDocsForErrors.length > 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++;
    }
  }

  // Generate permissions and command files from parsed command docs
  if (parsedCommandDocs.length > 0) {
    const commands = parsedCommandDocs.map((d) => ({
      name: d.commandName,
      permissionScope: d.permissionScope,
    }));
    fs.mkdirSync(libDir, { recursive: true });
    const permissionsContent = generatePermissions(moduleName, commands);
    const permissionsFile = path.join(libDir, "permissions.generated.ts");
    fs.writeFileSync(permissionsFile, permissionsContent);
    console.log(`  wrote ${path.relative(modulePath, permissionsFile)}`);
    generated++;

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

      const implFile = path.join(commandDir, `${doc.commandName}.ts`);
      if (!fs.existsSync(implFile)) {
        fs.writeFileSync(implFile, renderCommandStub(doc.commandName));
        console.log(`  scaffolded ${path.relative(modulePath, implFile)}`);
        generated++;
      }

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

  // Generate query files from parsed query docs
  if (parsedQueryDocs.length > 0) {
    const queryDir = path.join(modulePath, MODULE_PATHS.queries);
    fs.mkdirSync(queryDir, { recursive: true });

    for (const doc of parsedQueryDocs) {
      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, renderQueryStub(doc.commandName));
        console.log(`  scaffolded ${path.relative(modulePath, implFile)}`);
        generated++;
      }

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

  // Generate db stubs and lifecycle from parsed model docs
  if (parsedModelDocs.length > 0) {
    const dbDir = path.join(modulePath, MODULE_PATHS.db);
    fs.mkdirSync(dbDir, { recursive: true });

    for (const modelDoc of parsedModelDocs) {
      const camelName = modelDoc.modelName.charAt(0).toLowerCase() + modelDoc.modelName.slice(1);
      const dbFile = path.join(dbDir, `${camelName}.ts`);
      if (!fs.existsSync(dbFile)) {
        fs.writeFileSync(dbFile, renderDbStub(camelName));
        console.log(`  scaffolded ${path.relative(modulePath, dbFile)}`);
        generated++;
      }

      if (modelDoc.transitions.length > 0) {
        const lifecycleContent = generateLifecycle(modelDoc.modelName, modelDoc.transitions);
        const lifecycleFile = path.join(dbDir, `${camelName}.lifecycle.generated.ts`);
        fs.writeFileSync(lifecycleFile, lifecycleContent);
        console.log(`  wrote ${path.relative(modulePath, lifecycleFile)}`);
        generated++;
      }
    }
  }

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