import { context } from 'esbuild';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawn } from 'node:child_process';
import http from 'node:http';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const root = path.resolve(__dirname, '..');

const electronEnv = {
  ...process.env,
  NODE_ENV: 'development',
  DND_SKIP_LICENSE: process.env.DND_SKIP_LICENSE ?? '1',
  // Совпадает с vite.config.ts `server.host` (не localhost: на Windows он часто → ::1).
  VITE_DEV_SERVER_URL: 'http://127.0.0.1:5173/',
};

function spawnShell(command, opts = {}) {
  const child = spawn(command, {
    cwd: root,
    stdio: 'inherit',
    shell: true,
    ...opts,
  });
  child.on('exit', (code) => {
    if (code !== 0 && code !== null) {
      process.exitCode = code;
    }
  });
  return child;
}

/** Убивает дерево процессов (на Windows `child.kill()` часто не гасит Vite на 5173). */
function killTree(child) {
  if (!child || child.killed) return;
  if (typeof child.exitCode === 'number' && child.exitCode !== null) return;
  if (process.platform === 'win32' && child.pid) {
    spawn('taskkill', ['/PID', String(child.pid), '/T', '/F'], {
      stdio: 'ignore',
      windowsHide: true,
      detached: true,
    });
  } else {
    try {
      child.kill('SIGTERM');
    } catch {
      /* ignore */
    }
  }
}

function waitForVite(url = 'http://127.0.0.1:5173/editor.html', timeoutMs = 60000) {
  const started = Date.now();
  return new Promise((resolve, reject) => {
    const tick = () => {
      const req = http.get(url, (res) => {
        res.resume();
        resolve();
      });
      req.on('error', () => {
        if (Date.now() - started > timeoutMs) {
          reject(new Error(`Timed out waiting for Vite at ${url}`));
          return;
        }
        setTimeout(tick, 300);
      });
      req.setTimeout(2000, () => {
        req.destroy();
      });
    };
    tick();
  });
}

let shuttingDown = false;
let electron = null;
let vite = null;
let dispose = async () => {};
let restartTimer = null;
let electronStarted = false;
let restartingElectron = false;

async function shutdown() {
  if (shuttingDown) return;
  shuttingDown = true;
  if (restartTimer) clearTimeout(restartTimer);
  killTree(vite);
  killTree(electron);
  await dispose();
  process.exit(0);
}

function startElectron() {
  if (shuttingDown) return;
  restartingElectron = false;
  if (electron) killTree(electron);
  electron = spawnShell('npx electron .', { env: electronEnv });
  electron.once('exit', () => {
    if (restartingElectron || shuttingDown) return;
    void shutdown();
  });
  electronStarted = true;
}

function scheduleElectronRestart() {
  if (shuttingDown || !electronStarted) return;
  if (restartTimer) clearTimeout(restartTimer);
  restartTimer = setTimeout(() => {
    restartTimer = null;
    console.log('[dev] main/preload rebuilt — restarting Electron…');
    restartingElectron = true;
    startElectron();
  }, 300);
}

function createRestartPlugin() {
  return {
    name: 'restart-electron-on-rebuild',
    setup(build) {
      build.onEnd((result) => {
        if (result.errors.length === 0) scheduleElectronRestart();
      });
    },
  };
}

async function watchMainAndPreload() {
  const restartPlugin = createRestartPlugin();
  const main = await context({
    entryPoints: [path.join(root, 'app/main/index.ts')],
    outfile: path.join(root, 'dist/main/index.cjs'),
    platform: 'node',
    target: 'node22',
    format: 'cjs',
    bundle: true,
    sourcemap: true,
    external: ['electron', 'electron-updater', 'sharp', 'ffmpeg-static', 'classic-level'],
    define: { 'process.env.NODE_ENV': JSON.stringify('development') },
    plugins: [restartPlugin],
  });
  await main.rebuild();
  await main.watch();

  const preload = await context({
    entryPoints: [path.join(root, 'app/preload/index.ts')],
    outfile: path.join(root, 'dist/preload/index.cjs'),
    platform: 'node',
    target: 'node22',
    format: 'cjs',
    bundle: true,
    sourcemap: true,
    external: ['electron'],
    define: { 'process.env.NODE_ENV': JSON.stringify('development') },
    plugins: [restartPlugin],
  });
  await preload.rebuild();
  await preload.watch();

  return async () => {
    await Promise.all([main.dispose(), preload.dispose()]);
  };
}

dispose = await watchMainAndPreload();

vite = spawnShell('npx vite dev --strictPort', {
  env: electronEnv,
});

try {
  await waitForVite();
} catch (err) {
  console.error('[dev] Vite did not start in time:', err);
}

startElectron();

process.on('SIGINT', () => void shutdown());
process.on('SIGTERM', () => void shutdown());

vite.once('exit', (code) => {
  if (code !== 0 && code !== null && !shuttingDown) {
    void shutdown();
  }
});
