import fs from 'node:fs';
import https from 'node:https';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import 'dotenv/config';

import { connectDb, closeDb } from './src/db.js';
import { createJtlPosServer } from './src/jtl-server.js';
import { createPairingStore } from './src/pairing.js';
import { closeOrderLog } from './src/order-log.js';
import { closeRequestLog, logRequest } from './src/request-log.js';
import { logger } from './src/logger.js';
import { fetchActiveShop } from './src/shop.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const PORT = Number(process.env.PORT) || 4443;
const AUTH_TOKEN = process.env.AUTH_TOKEN || 'df40ad2067954646abb0499548a52241';
const PAIRING_CODE = process.env.PAIRING_CODE || '307018';

const certsDir = path.join(__dirname, 'certs');
const keyPath = path.join(certsDir, 'key.pem');
const certPath = path.join(certsDir, 'cert.pem');

if (!fs.existsSync(keyPath) || !fs.existsSync(certPath)) {
  logger.error('Missing TLS certificate. Run: npm run cert');
  process.exit(1);
}

const CONSOLE_URL_MAX_LENGTH = 100;

function truncateUrl(url) {
  if (url.length <= CONSOLE_URL_MAX_LENGTH) {
    return url;
  }
  return `${url.slice(0, CONSOLE_URL_MAX_LENGTH)}...`;
}

const lastInitState = { url: '', result: '', timestamp: 0 };
let suppressedCount = 0;

setInterval(() => {
  if (suppressedCount > 0) {
    logger.info(`Suppressed ${suppressedCount} init log(s) in the last hour`);
    suppressedCount = 0;
  }
}, 60 * 60 * 1000);

function shouldLogInit(url, statusCode, durationMs, responseBody) {
  if (statusCode !== 200) return true;
  if (durationMs > 400) return true;

  const now = Date.now();
  const timeSinceLast = now - lastInitState.timestamp;

  if (timeSinceLast < 30_000 && url === lastInitState.url && responseBody === lastInitState.result) {
    lastInitState.timestamp = now;
    suppressedCount++;
    return false;
  }

  lastInitState.url = url;
  lastInitState.result = responseBody;
  lastInitState.timestamp = now;
  return true;
}

function formatBody(buffer) {
  if (!buffer.length) {
    return '(empty)';
  }
  const text = buffer.toString('utf8');
  if (/^[\x09\x0A\x0D\x20-\x7E\u0080-\uFFFF]*$/.test(text)) {
    return text;
  }
  return `[binary ${buffer.length} bytes]`;
}

const pairingStore = createPairingStore();
pairingStore.setPairingCode(PAIRING_CODE, 'JTL-POS');
pairingStore.registerDevice(AUTH_TOKEN, 'JTL-POS');

const jtlHandler = createJtlPosServer(pairingStore, { authToken: AUTH_TOKEN });

const loggedJtlHandler = async (req, res) => {
  const started = Date.now();
  let responseBuffer = Buffer.alloc(0);

  const originalEnd = res.end.bind(res);
  res.end = (chunk, ...args) => {
    if (chunk) {
      responseBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
    }
    return originalEnd(chunk, ...args);
  };

  await jtlHandler(req, res);

  const responseBody = formatBody(responseBuffer);
  const durationMs = Date.now() - started;
  const isInit = req.url.startsWith('/api/v1/init');

  if (!isInit || shouldLogInit(req.url, res.statusCode, durationMs, responseBody)) {
    logger.info(`${req.socket.remoteAddress} ${req.method} ${truncateUrl(req.url)} ${res.statusCode} ${durationMs}ms`);

    logRequest({
      remoteAddress: req.socket.remoteAddress,
      method: req.method,
      url: req.url,
      statusCode: res.statusCode,
      durationMs,
      response: responseBody,
    });
  }
};

const httpsServer = https.createServer(
  {
    key: fs.readFileSync(keyPath),
    cert: fs.readFileSync(certPath),
  },
  loggedJtlHandler
);

async function start() {
  try {
    const pool = await connectDb();
    logger.success(`MSSQL connected: ${process.env.MSSQL_SERVER}/${process.env.MSSQL_DATABASE}`);
    const activeShop = await fetchActiveShop(pool);
    logger.info(`Active shop ID: ${activeShop}`);
  } catch (err) {
    logger.warn(`MSSQL connection skipped: ${err.message}`);
    logger.warn('POS handshake will still work; sync from database is not available yet.');
  }

  httpsServer.listen(PORT, '0.0.0.0', () => {
    logger.success(`HTTPS POS server listening on https://0.0.0.0:${PORT}`);
    logger.info(`Certificate: ${certPath}`);
    logger.info(`Pairing code: ${PAIRING_CODE}`);
    logger.info(`Auth token: ${AUTH_TOKEN}`);
  });
}

async function shutdown() {
  logger.info('Shutting down...');
  httpsServer.close();
  await closeDb();
  closeRequestLog();
  closeOrderLog();
}

process.on('SIGINT', () => {
  shutdown().finally(() => process.exit(0));
});
process.on('SIGTERM', () => {
  shutdown().finally(() => process.exit(0));
});

start();
