import path from 'path';
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import { ConfigSingleton } from './config/ConfigSingleton';
import { createContainer } from './container';
import { createApiRouter } from './routes/api';
import { errorHandler } from './middleware/errorHandler';
import { JobProcessorFactory } from './factories/JobProcessorFactory';
import { getPrisma } from './infrastructure/database/PrismaSingleton';

const cfg = ConfigSingleton.getInstance();
const container = createContainer();
const prisma = getPrisma();

const app = express();
app.use(
  helmet({
    contentSecurityPolicy: false,
    // Allow the Angular SPA (different origin/port) to read API responses.
    crossOriginResourcePolicy: { policy: 'cross-origin' },
  }),
);

const allowedOrigins = new Set(
  [
    cfg.frontendUrl,
    'http://localhost:4200',
    'http://127.0.0.1:4200',
    'http://localhost:4300',
    'http://127.0.0.1:4300',
  ].filter(Boolean),
);

app.use(
  cors({
    origin(origin, callback) {
      if (!origin || allowedOrigins.has(origin)) {
        callback(null, true);
        return;
      }
      callback(null, false);
    },
    credentials: true,
  }),
);
app.use(express.json());

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: cfg.maxLoginAttempts,
  message: { code: 'RATE_LIMITED', message: 'Too many requests' },
});

app.use('/api/v1/employee/request-token', authLimiter);
app.use('/api/v1', createApiRouter(container));
app.use(errorHandler);

JobProcessorFactory.registerSchedulers(container);

async function connectDatabase(retries = 5, delayMs = 3000): Promise<void> {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      await prisma.$connect();
      await prisma.$queryRaw`SELECT 1`;
      console.log('Database connected');
      return;
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      console.error(`[STARTUP] Database attempt ${attempt}/${retries} failed: ${msg}`);
      if (attempt === retries) {
        console.error(
          '[STARTUP] Cannot reach MySQL (see DATABASE_URL in backend/.env). ' +
            'Check network/VPN and that MySQL is running on the remote host, then restart the API.',
        );
        throw err;
      }
      await new Promise((resolve) => setTimeout(resolve, delayMs));
    }
  }
}

async function verifyShiftSetupSchema() {
  try {
    await prisma.$queryRaw`SELECT 1 FROM team_shift_verifier_assignments LIMIT 1`;
    console.log('Shift setup schema OK (team_shift_verifier_assignments)');
  } catch {
    console.error(
      '[STARTUP] Missing table team_shift_verifier_assignments. HR shift verifier assignment will fail until you run:\n' +
        '  cd backend && npx prisma migrate deploy && npm run build\n' +
        'Then restart this Node process (kill the old one on port ' +
        cfg.port +
        ' first).',
    );
  }
}

async function start() {
  await connectDatabase();
  app.listen(cfg.port, '0.0.0.0', () => {
    console.log(`Leaves API running on http://localhost:${cfg.port}/api/v1`);
    void verifyShiftSetupSchema();
  });
}

start().catch((err) => {
  console.error(err);
  process.exit(1);
});

export default app;
