import fs from 'fs';
import path from 'path';
import { spawnSync } from 'child_process';

type CheckResult = {
  name: string;
  ok: boolean;
  detail: string;
};

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

async function httpJson(url: string) {
  const response = await fetch(url, { method: 'GET' });
  const text = await response.text();
  let json: unknown = null;
  try {
    json = text ? JSON.parse(text) : null;
  } catch {
    json = text;
  }
  return { ok: response.ok, status: response.status, body: json };
}

function findBrowserExecutable() {
  const candidates = [
    'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
    'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
    'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
    'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
  ];

  return candidates.find((item) => fs.existsSync(item)) ?? null;
}

function runBrowserDomCheck(url: string) {
  const browserPath = findBrowserExecutable();
  if (!browserPath) {
    return {
      ok: false,
      detail: 'no Chrome/Edge executable found for browser smoke test',
    };
  }

  const result = spawnSync(
    browserPath,
    [
      '--headless=new',
      '--disable-gpu',
      '--virtual-time-budget=8000',
      '--dump-dom',
      url,
    ],
    {
      encoding: 'utf8',
      timeout: 30000,
    },
  );

  if (result.error) {
    return {
      ok: false,
      detail: result.error.message,
    };
  }

  if (result.status !== 0) {
    return {
      ok: false,
      detail: result.stderr?.trim() || `browser exited with code ${result.status ?? 'unknown'}`,
    };
  }

  const dom = result.stdout ?? '';
  const hasBackendUnavailable = dom.includes('暂时无法连接后端服务');
  const hasLoopbackLeak = dom.includes('127.0.0.1:3001') || dom.includes('localhost:3001');
  const hasAlertsView =
    dom.includes('告警事件') &&
    dom.includes('当前告警池') &&
    !dom.includes('Internal server error');

  if (!hasAlertsView || hasBackendUnavailable || hasLoopbackLeak) {
    return {
      ok: false,
      detail: hasBackendUnavailable || hasLoopbackLeak
        ? 'browser rendered backend-unavailable state or loopback API leak'
        : 'browser DOM did not reach the alerts screen',
    };
  }

  return {
    ok: true,
    detail: `browser rendered alerts page successfully via ${url}`,
  };
}

async function checkDatabase(env: Record<string, string>) {
  const mysql = await import('mysql2/promise');
  const connection = await mysql.createConnection({
    host: env.DB_HOST ?? '127.0.0.1',
    port: Number(env.DB_PORT ?? '3306'),
    user: env.DB_USERNAME ?? 'sentai',
    password: env.DB_PASSWORD ?? 'sentai123',
    database: env.DB_DATABASE ?? 'sentai',
  });

  const requiredTables = [
    'monitor_agent',
    'monitor_target',
    'monitor_run',
    'monitor_log_event',
    'monitor_alert',
  ];
  const results: CheckResult[] = [];

  for (const table of requiredTables) {
    const [rows] = await connection.query(`SHOW TABLES LIKE '${table}'`);
    results.push({
      name: `db:${table}`,
      ok: Array.isArray(rows) && rows.length > 0,
      detail:
        Array.isArray(rows) && rows.length > 0
          ? 'table exists'
          : 'table missing',
    });
  }

  await connection.end();
  return results;
}

async function main() {
  const root = process.cwd();
  const env = {
    ...parseEnvFile(path.join(root, '.env')),
    ...process.env,
  } as Record<string, string>;

  const apiBase =
    (env.NEXT_PUBLIC_API_BASE_URL || env.APP_API_BASE_URL || 'http://127.0.0.1:3001').replace(
      /\/$/,
      '',
    );
  const proxyBase = (env.SELF_CHECK_PROXY_BASE_URL || 'http://127.0.0.1:3010').replace(/\/$/, '');
  const browserUrl = (env.SELF_CHECK_BROWSER_URL || `${proxyBase}/alerts`).replace(/\/$/, '');

  const results: CheckResult[] = [];

  try {
    results.push(...(await checkDatabase(env)));
  } catch (error) {
    results.push({
      name: 'db:connection',
      ok: false,
      detail: error instanceof Error ? error.message : 'database check failed',
    });
  }

  const endpoints = [
    { name: 'api:health', url: `${apiBase}/health` },
    { name: 'api:monitoring-summary', url: `${apiBase}/api/monitoring/summary` },
    { name: 'api:monitoring-alerts', url: `${apiBase}/api/monitoring/alerts?limit=5` },
    { name: 'api:monitoring-runs', url: `${apiBase}/api/monitoring/runs?limit=5` },
    { name: 'proxy:health', url: `${proxyBase}/health` },
    { name: 'proxy:monitoring-alerts', url: `${proxyBase}/api/monitoring/alerts?limit=5` },
  ];

  for (const endpoint of endpoints) {
    try {
      const response = await httpJson(endpoint.url);
      results.push({
        name: endpoint.name,
        ok: response.ok,
        detail: response.ok
          ? `HTTP ${response.status}`
          : `HTTP ${response.status} ${typeof response.body === 'string' ? response.body : JSON.stringify(response.body)}`,
      });
    } catch (error) {
      results.push({
        name: endpoint.name,
        ok: false,
        detail: error instanceof Error ? error.message : 'request failed',
      });
    }
  }

  const browserCheck = runBrowserDomCheck(browserUrl);
  results.push({
    name: 'browser:alerts-page',
    ok: browserCheck.ok,
    detail: browserCheck.detail,
  });

  const failed = results.filter((item) => !item.ok);
  const passed = results.filter((item) => item.ok);

  console.log('SentAI Self Check');
  console.log(`API Base: ${apiBase}`);
  console.log(`Proxy Base: ${proxyBase}`);
  console.log(`Browser URL: ${browserUrl}`);
  console.log('');
  for (const item of results) {
    console.log(`${item.ok ? '[PASS]' : '[FAIL]'} ${item.name} - ${item.detail}`);
  }
  console.log('');
  console.log(`Summary: ${passed.length} passed, ${failed.length} failed`);

  if (failed.length) {
    process.exitCode = 1;
  }
}

void main();
