import 'dotenv/config';
import * as http from 'http';
import { Pool } from 'pg';
import { scrapeAll } from './scraper';
import { syncToDatabase } from './db';

// In-memory log ring buffer — last 500 lines captured from all modules
const LOG_BUFFER_MAX = 500;
const logBuffer: Array<{ ts: string; line: string }> = [];

const _origLog = console.log;
const _origErr = console.error;
function captureToBuffer(line: string) {
  logBuffer.push({ ts: new Date().toISOString(), line });
  if (logBuffer.length > LOG_BUFFER_MAX) logBuffer.shift();
}
console.log = (...args: unknown[]) => {
  const line = args.map(String).join(' ');
  _origLog(line);
  captureToBuffer(line);
};
console.error = (...args: unknown[]) => {
  const line = args.map(String).join(' ');
  _origErr(line);
  captureToBuffer(line);
};

function log(msg: string) {
  console.log(`[sync] ${new Date().toISOString()} ${msg}`);
}

let syncRunning = false;

function requireEnv(name: string): string {
  const val = process.env[name];
  if (!val) throw new Error(`Missing required environment variable: ${name}`);
  return val;
}

/** Returns milliseconds until the next midnight (00:00:00) in local time. */
function msUntilMidnight(): number {
  const now = new Date();
  const midnight = new Date(now);
  midnight.setDate(now.getDate() + 1);
  midnight.setHours(0, 0, 0, 0);
  return midnight.getTime() - now.getTime();
}

async function runSync(force = false): Promise<void> {
  if (syncRunning) {
    log('Sync already in progress, skipping');
    return;
  }
  syncRunning = true;
  const username = requireEnv('FDISK_USERNAME');
  const password = requireEnv('FDISK_PASSWORD');

  const pool = new Pool({
    host:     requireEnv('DB_HOST'),
    port:     parseInt(process.env.DB_PORT ?? '5432'),
    database: requireEnv('DB_NAME'),
    user:     requireEnv('DB_USER'),
    password: requireEnv('DB_PASSWORD'),
  });

  try {
    if (force) log('Force mode: ON');
    log('Starting FDISK sync');

    const { members, ausbildungen, befoerderungen, untersuchungen, fahrgenehmigungen } = await scrapeAll(username, password);
    await syncToDatabase(pool, members, ausbildungen, befoerderungen, untersuchungen, fahrgenehmigungen, force);
    log(`Sync complete — ${members.length} members, ${ausbildungen.length} Ausbildungen, ${befoerderungen.length} Beförderungen, ${untersuchungen.length} Untersuchungen, ${fahrgenehmigungen.length} Fahrgenehmigungen`);
  } finally {
    syncRunning = false;
    await pool.end();
  }
}

function startHttpServer(port: number) {
  const server = http.createServer((req, res) => {
    res.setHeader('Content-Type', 'application/json');

    if (req.method === 'GET' && req.url === '/logs') {
      res.writeHead(200);
      res.end(JSON.stringify({ running: syncRunning, logs: logBuffer }));
    } else if (req.method === 'POST' && req.url === '/trigger') {
      if (syncRunning) {
        res.writeHead(409);
        res.end(JSON.stringify({ running: true, message: 'Sync already in progress' }));
        return;
      }
      let body = '';
      req.on('data', (chunk: Buffer) => { body += chunk.toString(); });
      req.on('end', () => {
        let force = false;
        try {
          const parsed = JSON.parse(body);
          force = parsed?.force === true;
        } catch {
          // no body or invalid JSON — force stays false
        }
        res.writeHead(200);
        res.end(JSON.stringify({ started: true, force }));
        runSync(force).catch(err => log(`ERROR during manual sync: ${err.message}`));
      });
    } else {
      res.writeHead(404);
      res.end(JSON.stringify({ message: 'Not found' }));
    }
  });
  server.listen(port, () => log(`HTTP control server listening on port ${port}`));
}

async function main(): Promise<void> {
  log('FDISK sync service started');

  const httpPort = parseInt(process.env.SYNC_HTTP_PORT ?? '3001', 10);
  startHttpServer(httpPort);

  // Run once immediately on startup so the first sync doesn't wait until midnight
  await runSync().catch(err => log(`ERROR during initial sync: ${err.message}`));

  // Then schedule at midnight every day
  while (true) {
    const delay = msUntilMidnight();
    const nextRun = new Date(Date.now() + delay);
    log(`Next sync scheduled at ${nextRun.toLocaleString()} (in ${Math.round(delay / 60000)} min)`);
    await new Promise(r => setTimeout(r, delay));
    await runSync().catch(err => log(`ERROR during scheduled sync: ${err.message}`));
  }
}

main().catch(err => {
  console.error(`[sync] Fatal error: ${err.message}`);
  process.exit(1);
});
