import crypto from 'node:crypto';
import fs from 'node:fs';
import http from 'node:http';
import net from 'node:net';
import path from 'node:path';
import { spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { inspectDatabaseFile } from '../server/src/database-health.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(__dirname, '..');
process.chdir(rootDir);

function assertNodeVersion() {
  const [major, minor] = process.versions.node.split('.').map(Number);
  if (major < 22 || (major === 22 && minor < 16)) {
    throw new Error(`OnlyCoin için Node.js 22.16 veya üzeri gerekir. Mevcut sürüm: ${process.versions.node}`);
  }
}

function parseEnvFile(filePath) {
  if (!fs.existsSync(filePath)) return {};
  const output = {};
  for (const rawLine of fs.readFileSync(filePath, 'utf8').split(/\r?\n/)) {
    const line = rawLine.trim();
    if (!line || line.startsWith('#')) continue;
    const index = line.indexOf('=');
    if (index < 1) continue;
    const key = line.slice(0, index).trim();
    let value = line.slice(index + 1).trim();
    if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
      value = value.slice(1, -1).replace(/\\n/g, '\n').replace(/\\r/g, '\r').replace(/\\"/g, '"').replace(/\\\\/g, '\\');
    }
    output[key] = value;
  }
  return output;
}

function bool(value, fallback = false) {
  if (value == null || value === '') return fallback;
  return ['1', 'true', 'yes', 'on', 'evet'].includes(String(value).trim().toLowerCase());
}

function canListen(port, host = '127.0.0.1') {
  return new Promise((resolve) => {
    const server = net.createServer();
    server.once('error', () => resolve(false));
    server.once('listening', () => server.close(() => resolve(true)));
    server.listen(port, host);
  });
}

function getHealth(port) {
  return new Promise((resolve) => {
    const request = http.get({ hostname: '127.0.0.1', port, path: '/api/health', timeout: 1000 }, (response) => {
      const chunks = [];
      response.on('data', (chunk) => chunks.push(chunk));
      response.on('end', () => {
        try {
          const body = JSON.parse(Buffer.concat(chunks).toString('utf8'));
          resolve({ ok: response.statusCode === 200 && body?.service === 'onlycoin-api', body });
        } catch { resolve({ ok: false, body: null }); }
      });
    });
    request.once('timeout', () => { request.destroy(); resolve({ ok: false, body: null }); });
    request.once('error', () => resolve({ ok: false, body: null }));
  });
}

function openBrowser(url) {
  if (bool(process.env.NO_BROWSER, false)) return;
  try {
    let child;
    if (process.platform === 'win32') child = spawn('cmd', ['/c', 'start', '', url], { detached: true, stdio: 'ignore', windowsHide: true });
    else if (process.platform === 'darwin') child = spawn('open', [url], { detached: true, stdio: 'ignore' });
    else child = spawn('xdg-open', [url], { detached: true, stdio: 'ignore' });
    child.unref();
  } catch { /* The URL is printed for manual opening. */ }
}

function ensureInstallerToken(dataDir) {
  fs.mkdirSync(dataDir, { recursive: true });
  const lockPath = path.join(dataDir, 'installed.json');
  if (fs.existsSync(lockPath)) return '';
  const tokenPath = path.join(dataDir, 'install.token');
  if (!fs.existsSync(tokenPath)) {
    fs.writeFileSync(tokenPath, crypto.randomBytes(24).toString('base64url'), { encoding: 'utf8', mode: 0o600 });
    try { fs.chmodSync(tokenPath, 0o600); } catch { /* Windows */ }
  }
  return fs.readFileSync(tokenPath, 'utf8').trim();
}

function validateDatabase(dataDir, installed) {
  const dbPath = path.join(dataDir, 'onlycoin.db');
  const health = inspectDatabaseFile(dbPath);
  if (['missing', 'empty', 'healthy'].includes(health.status)) return;
  const message = [
    'OnlyCoin veritabanı güvenli şekilde açılamadı.',
    `Durum: ${health.status}`,
    health.details ? `Açıklama: ${health.details}` : '',
    installed
      ? 'Canlı veritabanı otomatik sıfırlanmadı. Sunucuyu kapatın, data klasörünü yedekleyin ve VERITABANI-KONTROL-WINDOWS.bat ile kontrol edin.'
      : 'Kurulum henüz tamamlanmadı. Bozuk dosyayı yedekledikten sonra data/onlycoin.db, .db-wal ve .db-shm dosyalarını kaldırıp kurulumu yeniden başlatabilirsiniz.'
  ].filter(Boolean).join('\n');
  throw new Error(message);
}

async function waitForHealth(port, child) {
  for (let attempt = 0; attempt < 120; attempt += 1) {
    if (child.exitCode != null) return { exited: true, code: child.exitCode };
    const status = await getHealth(port);
    if (status.ok) return { exited: false, status: status.body };
    await new Promise((resolve) => setTimeout(resolve, 250));
  }
  throw new Error('Sunucu 30 saniye içinde sağlık kontrolüne cevap vermedi.');
}

async function runServerCycle({ firstLaunch = false } = {}) {
  const runtimeEnvPath = path.resolve(process.env.ONLYCOIN_ENV_PATH || path.join(rootDir, '.env'));
  const fileEnv = parseEnvFile(runtimeEnvPath);
  const dataDir = path.resolve(rootDir, fileEnv.DATA_DIR || process.env.DATA_DIR || './data');
  const installed = fs.existsSync(path.join(dataDir, 'installed.json'));
  const port = Number.parseInt(fileEnv.PORT || process.env.PORT || '8787', 10) || 8787;
  const listenHost = fileEnv.HOST || process.env.HOST || '0.0.0.0';
  const browserHost = '127.0.0.1';
  const baseUrl = `http://${browserHost}:${port}`;

  validateDatabase(dataDir, installed);
  const health = await getHealth(port);
  if (health.ok) {
    const url = health.body?.installed ? `${baseUrl}/` : `${baseUrl}/install/`;
    console.log(`OnlyCoin zaten çalışıyor: ${url}`);
    openBrowser(url);
    return { alreadyRunning: true, code: 0 };
  }
  if (!(await canListen(port, browserHost))) throw new Error(`${port} portu başka bir uygulama tarafından kullanılıyor.`);

  const installToken = ensureInstallerToken(dataDir);
  const childEnv = {
    ...process.env,
    ...fileEnv,
    HOST: listenHost,
    PORT: String(port),
    DATA_DIR: fileEnv.DATA_DIR || process.env.DATA_DIR || './data'
  };
  const child = spawn(process.execPath, ['--no-warnings', 'server/src/main.js'], {
    cwd: rootDir,
    env: childEnv,
    stdio: 'inherit',
    windowsHide: false
  });

  const stop = () => { if (!child.killed) child.kill('SIGTERM'); };
  process.once('SIGINT', stop);
  process.once('SIGTERM', stop);

  const ready = await waitForHealth(port, child);
  if (ready.exited) return { alreadyRunning: false, code: ready.code ?? 1 };

  const runtimeInstalled = Boolean(ready.status?.installed);
  const destination = runtimeInstalled
    ? `${baseUrl}/`
    : `${baseUrl}/install/?token=${encodeURIComponent(installToken)}`;

  console.log('\n=========================================================');
  console.log(runtimeInstalled ? ' ONLYCOIN CANLI SUNUCU ÇALIŞIYOR' : ' ONLYCOIN 4 AŞAMALI KURULUM SİHİRBAZI');
  console.log('=========================================================');
  console.log(`Adres: ${destination}`);
  if (runtimeInstalled) {
    console.log(`Oyun:  ${baseUrl}/game/`);
    console.log(`Admin: ${baseUrl}/admin/`);
  } else {
    console.log(`Kurulum anahtarı: ${installToken}`);
    console.log('Kurulum tamamlanmadan kullanıcı, finans veya yönetici verisi oluşturulmaz.');
  }
  console.log('Kapatmak için CTRL+C tuşlarına basın.');
  console.log('=========================================================\n');
  if (firstLaunch || !runtimeInstalled) openBrowser(destination);

  const code = await new Promise((resolve, reject) => {
    child.once('error', reject);
    child.once('exit', (exitCode, signal) => resolve(exitCode ?? (signal ? 1 : 0)));
  });
  process.removeListener('SIGINT', stop);
  process.removeListener('SIGTERM', stop);
  return { alreadyRunning: false, code };
}

async function main() {
  assertNodeVersion();
  let firstLaunch = true;
  while (true) {
    const result = await runServerCycle({ firstLaunch });
    firstLaunch = false;
    if (result.alreadyRunning) return;
    if (result.code === 75) {
      console.log('\nKurulum yapılandırması etkinleştiriliyor; sunucu yeniden başlatılıyor...\n');
      await new Promise((resolve) => setTimeout(resolve, 800));
      continue;
    }
    if (result.code !== 0) throw new Error(`OnlyCoin sunucusu ${result.code} çıkış koduyla kapandı.`);
    return;
  }
}

main().catch((error) => {
  console.error('\n[ONLYCOIN BAŞLATMA HATASI]');
  console.error(error?.stack || error?.message || String(error));
  process.exitCode = 1;
});
