import 'dotenv/config';

import { serve, type ServerType } from '@hono/node-server';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

import {
  captureDenoServeHandler,
  getCapturedDenoServeHandler,
  installDenoShim,
  type LegacyFetchHandler,
} from './runtime/deno-shim.js';

installDenoShim();

const legacyEntrypoints = [
  {
    file: 'index.tsx',
    name: 'main',
    prefix: '/make-server-7249dcd9',
  },
  {
    file: 'admin-standalone.tsx',
    name: 'admin',
    prefix: '/admin-server-7249dcd9',
  },
  {
    file: 'rh-standalone.tsx',
    name: 'rh',
    prefix: '/rh-server-7249dcd9',
  },
  {
    file: 'saft-standalone.tsx',
    name: 'saft',
    prefix: '/saft-server-7249dcd9',
  },
  {
    file: 'revendedores-standalone.tsx',
    name: 'revendedores',
    prefix: '/revendedores-server-7249dcd9',
  },
] as const;

export type LegacyEntrypointName = (typeof legacyEntrypoints)[number]['name'];

async function loadLegacyEntrypoint(
  entrypoint: (typeof legacyEntrypoints)[number],
): Promise<LegacyFetchHandler> {
  const generatedEntryUrl = new URL(
    `../generated/server/${entrypoint.file}`,
    import.meta.url,
  );

  try {
    return await captureDenoServeHandler(entrypoint.name, () =>
      import(generatedEntryUrl.href),
    );
  } catch (error) {
    const errorCode = (error as NodeJS.ErrnoException).code;
    if (errorCode === 'ERR_MODULE_NOT_FOUND' || errorCode === 'MODULE_NOT_FOUND') {
      throw new Error(
        'Legacy runtime is not synchronized. Run `npx tsx scripts/sync-legacy-runtime.ts`.',
        { cause: error },
      );
    }
    throw error;
  }
}

for (const entrypoint of legacyEntrypoints) {
  await loadLegacyEntrypoint(entrypoint);
}

export const legacyHandlers: Readonly<Record<LegacyEntrypointName, LegacyFetchHandler>> =
  Object.freeze({
    main: getCapturedDenoServeHandler('main'),
    admin: getCapturedDenoServeHandler('admin'),
    rh: getCapturedDenoServeHandler('rh'),
    saft: getCapturedDenoServeHandler('saft'),
    revendedores: getCapturedDenoServeHandler('revendedores'),
  });

function matchesPrefix(pathname: string, prefix: string): boolean {
  return pathname === prefix || pathname.startsWith(`${prefix}/`);
}

function dispatcherNotFound(request: Request): Response {
  const url = new URL(request.url);
  return Response.json(
    {
      success: false,
      error: 'Route not found',
      path: url.pathname,
      method: request.method,
    },
    { status: 404 },
  );
}

export const legacyFetch: LegacyFetchHandler = (request, ...arguments_) => {
  const pathname = new URL(request.url).pathname;

  for (const entrypoint of legacyEntrypoints) {
    if (matchesPrefix(pathname, entrypoint.prefix)) {
      return legacyHandlers[entrypoint.name](request, ...arguments_);
    }
  }

  return dispatcherNotFound(request);
};

let activeServer: ServerType | undefined;

export type LegacyServerOptions = {
  hostname?: string;
  onListen?: (address: string, port: number) => void;
  port?: number;
};

function environmentPort(): number {
  const rawPort = process.env.LEGACY_BACKEND_PORT ?? process.env.BACKEND_PORT ?? '3001';
  const port = Number(rawPort);
  if (!Number.isInteger(port) || port < 1 || port > 65_535) {
    throw new Error(`Invalid legacy backend port: ${rawPort}`);
  }
  return port;
}

export function startLegacyServer(options: LegacyServerOptions = {}): ServerType {
  if (activeServer) return activeServer;

  const hostname =
    options.hostname ??
    process.env.LEGACY_BACKEND_HOST ??
    process.env.BACKEND_HOST ??
    '127.0.0.1';
  const port = options.port ?? environmentPort();

  activeServer = serve(
    {
      fetch: legacyFetch,
      hostname,
      port,
    },
    (info) => {
      options.onListen?.(info.address, info.port);
    },
  );
  activeServer.once('close', () => {
    activeServer = undefined;
  });

  return activeServer;
}

function isDirectExecution(): boolean {
  const entryArgument = process.argv[1];
  if (!entryArgument) return false;

  const currentFile = path.resolve(fileURLToPath(import.meta.url));
  const entryFile = path.resolve(entryArgument);
  return process.platform === 'win32'
    ? currentFile.toLowerCase() === entryFile.toLowerCase()
    : currentFile === entryFile;
}

if (isDirectExecution()) {
  startLegacyServer({
    onListen: (address, port) => {
      console.log(`Tonline ERP legacy backend listening on http://${address}:${port}`);
    },
  });
}

export default legacyFetch;
