#!/usr/bin/env node --no-warnings
import { Command } from "@commander-js/extra-typings";
import type { Database, FFetchOptions } from "@proofkit/fmodata";
import { FMServerConnection } from "@proofkit/fmodata";
import { logger } from "better-auth";
import { getSchema } from "better-auth/db";
import chalk from "chalk";
import fs from "fs-extra";
import prompts from "prompts";
import { getConfig } from "../better-auth-cli/utils/get-config";
import { executeMigration, planMigration, prettyPrintMigrationPlan } from "../migrate";
import "dotenv/config";

async function main() {
  const program = new Command();

  program
    .command("migrate", { isDefault: true })
    .option("--cwd <path>", "Path to the current working directory", process.cwd())
    .option("--config <path>", "Path to the config file")
    .option("-u, --username <username>", "Full Access Username")
    .option("-p, --password <password>", "Full Access Password")
    .option("-y, --yes", "Skip confirmation", false)

    .action(async (options) => {
      const cwd = options.cwd;
      if (!fs.existsSync(cwd)) {
        logger.error(`The directory "${cwd}" does not exist.`);
        process.exit(1);
      }

      const config = await getConfig({
        cwd,
        configPath: options.config,
      });
      if (!config) {
        logger.error(
          "No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag.",
        );
        return;
      }

      // Resolve adapter directly (getAdapter removed in Better Auth 1.5)
      const databaseFactory = config.database;
      if (!databaseFactory || typeof databaseFactory !== "function") {
        logger.error("No database adapter found in auth config.");
        process.exit(1);
      }
      let adapter: { id?: string; database?: unknown };
      try {
        adapter = (databaseFactory as (opts: unknown) => { id?: string; database?: unknown })(config);
      } catch (e) {
        logger.error(e instanceof Error ? e.message : String(e));
        process.exit(1);
      }

      if (adapter?.id !== "filemaker") {
        logger.error("This generator is only compatible with the FileMaker adapter.");
        return;
      }

      const betterAuthSchema = getSchema(config);

      // Extract Database from the adapter factory or resolved adapter.
      // config.database is the FileMakerAdapter factory function (has .database set on it).
      // adapter is the resolved adapter after getAdapter() calls the factory (also has .database).
      // Try both: adapter first (post-call), then config.database (pre-call / factory function).
      const configDb =
        (adapter as unknown as { database?: Database }).database ??
        (config.database as unknown as { database?: Database } | undefined)?.database;
      if (!configDb || typeof configDb !== "object" || !("schema" in configDb)) {
        logger.error(
          "Could not extract Database instance from adapter. Ensure your auth.ts uses FileMakerAdapter with an fmodata Database.",
        );
        process.exit(1);
      }
      let db: Database = configDb;

      // Extract database name and server URL for display.
      // Try the public getter first (_getDatabaseName), fall back to the private field (databaseName).
      const dbObj = configDb as unknown as {
        _getDatabaseName?: string;
        databaseName?: string;
        context?: { _getBaseUrl?: () => string; _fetchClientOptions?: unknown };
      };
      const dbName: string = dbObj._getDatabaseName ?? dbObj.databaseName ?? "";
      const baseUrl: string | undefined = dbObj.context?._getBaseUrl?.();
      const serverUrl = baseUrl ? new URL(baseUrl).origin : undefined;

      // If CLI credential overrides are provided, construct a new connection
      if (options.username && options.password) {
        if (!dbName) {
          logger.error("Could not determine database filename from adapter config.");
          process.exit(1);
        }

        if (!baseUrl) {
          logger.error(
            "Could not determine server URL from adapter config. Ensure your auth.ts uses FMServerConnection.",
          );
          process.exit(1);
        }

        const fetchClientOptions = dbObj.context?._fetchClientOptions as FFetchOptions | undefined;
        const connection = new FMServerConnection({
          serverUrl: serverUrl as string,
          auth: {
            username: options.username,
            password: options.password,
          },
          fetchClientOptions,
        });

        db = connection.database(dbName);
      }

      let migrationPlan: Awaited<ReturnType<typeof planMigration>>;
      try {
        migrationPlan = await planMigration(db, betterAuthSchema);
      } catch (err) {
        logger.error(`Failed to read database schema: ${err instanceof Error ? err.message : err}`);
        process.exit(1);
      }

      if (migrationPlan.length === 0) {
        logger.info("No changes to apply. Database is up to date.");
        return;
      }

      if (!options.yes) {
        prettyPrintMigrationPlan(migrationPlan, { serverUrl, fileName: dbName });

        if (migrationPlan.length > 0) {
          console.log(chalk.gray("💡 Tip: You can use the --yes flag to skip this confirmation."));
        }

        const { confirm } = await prompts({
          type: "confirm",
          name: "confirm",
          message: "Apply the above changes to your database?",
        });
        if (!confirm) {
          logger.error("Schema changes not applied.");
          return;
        }
      }

      try {
        await executeMigration(db, migrationPlan);
        logger.info("Migration applied successfully.");
      } catch {
        process.exit(1);
      }
    });
  await program.parseAsync(process.argv);
  process.exit(0);
}

main().catch((err) => {
  logger.error(err.message ?? err);
  process.exit(1);
});
