import express from 'express';
import { createServer } from 'http';
import { toNodeHandler } from 'better-auth/node';
import cors from 'cors';
import helmet from 'helmet';
import swaggerUi from 'swagger-ui-express';

import { auth } from './auth';
import { buildOpenApiDocument } from './docs/openapi';
import videosRoutes from './routes/videos';
import adminRoutes from './routes/admin';
import devicesRoutes from './routes/devices';
import deviceLinksRoutes from './routes/device-links';
import commandsRoutes from './routes/commands';
import eventsRoutes from './routes/events';
import streamsRoutes from './routes/streams';
import recordingsRoutes from './routes/recordings';
import pushNotificationsRoutes from './routes/push-notifications';
import auditRoutes from './routes/audit';
import opsRoutes from './routes/ops';
import { rateLimit } from './middleware/security';
import { requestContext } from './middleware/observability';
import { setupRealtimeGateway } from './realtime/gateway';
import { ensureMinioBucket } from './utils/minio';
import { startRecordingsWorker } from './workers/recordings';
import { startPushWorker } from './services/push';

const app = express();
const openApiDocument = buildOpenApiDocument();
const trustedOrigins = process.env.BETTER_AUTH_TRUSTED_ORIGINS
  ? process.env.BETTER_AUTH_TRUSTED_ORIGINS.split(',').map((origin) => origin.trim()).filter(Boolean)
  : [];
const corsMiddleware = cors({
  origin: trustedOrigins.length > 0 ? trustedOrigins : true,
  credentials: true,
});

const buildMinioConnectOrigin = (): string | null => {
  const endpoint = process.env.MINIO_ENDPOINT?.trim();
  if (!endpoint) {
    return null;
  }

  if (endpoint.startsWith('http://') || endpoint.startsWith('https://')) {
    try {
      return new URL(endpoint).origin;
    } catch {
      return null;
    }
  }

  const useSSL = (process.env.MINIO_USE_SSL ?? 'false').toLowerCase() === 'true';
  const port = Number(process.env.MINIO_PORT ?? (useSSL ? 443 : 80));
  const scheme = useSSL ? 'https' : 'http';
  const includePort = !(useSSL && port === 443) && !(!useSSL && port === 80);

  return `${scheme}://${endpoint}${includePort ? `:${port}` : ''}`;
};

const minioConnectOrigin = buildMinioConnectOrigin();
const connectSrcDirectives = ["'self'", 'cdn.jsdelivr.net', ...(minioConnectOrigin ? [minioConnectOrigin] : [])];
const mediaSrcDirectives = ["'self'", 'blob:', 'data:', ...(minioConnectOrigin ? [minioConnectOrigin] : [])];

app.get('/', (_req, res) => {
  res.send('API is running');
});

app.get('/favicon.ico', (_req, res) => {
  res.status(204).end();
});

app.get('/openapi.json', (_req, res) => {
  res.json(openApiDocument);
});

app.use('/docs', swaggerUi.serve, swaggerUi.setup(openApiDocument));

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        ...helmet.contentSecurityPolicy.getDefaultDirectives(),
        "script-src": ["'self'", "'unsafe-inline'", "cdn.jsdelivr.net", "cdn.tailwindcss.com"],
        "style-src": ["'self'", "'unsafe-inline'", "cdn.jsdelivr.net", "fonts.googleapis.com"],
        "font-src": ["'self'", "fonts.gstatic.com"],
        "connect-src": connectSrcDirectives,
        "media-src": mediaSrcDirectives,
        "img-src": ["'self'", "data:", "blob:"],
      },
    },
  }),
);
app.use(corsMiddleware);
app.all('/api/auth/*splat', corsMiddleware, toNodeHandler(auth));
app.use(rateLimit({ keyPrefix: 'global', windowMs: 60_000, max: 400 }));
app.use(requestContext);
app.use(express.json());
app.use('/sim', express.static('public'));
app.use('/videos', videosRoutes);
app.use('/admin', adminRoutes);
app.use('/devices', rateLimit({ keyPrefix: 'devices', windowMs: 60_000, max: 120 }), devicesRoutes);
app.use('/device-links', deviceLinksRoutes);
app.use('/commands', rateLimit({ keyPrefix: 'commands', windowMs: 60_000, max: 120 }), commandsRoutes);
app.use('/events', rateLimit({ keyPrefix: 'events', windowMs: 60_000, max: 120 }), eventsRoutes);
app.use('/streams', rateLimit({ keyPrefix: 'streams', windowMs: 60_000, max: 120 }), streamsRoutes);
app.use('/recordings', rateLimit({ keyPrefix: 'recordings', windowMs: 60_000, max: 120 }), recordingsRoutes);
app.use('/push-notifications', pushNotificationsRoutes);
app.use('/audit', auditRoutes);
app.use('/ops', opsRoutes);

app.use((err: unknown, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
  console.error(err);
  res.status(500).json({ message: 'Internal server error' });
});

const port = Number(process.env.PORT ?? 3000);
const server = createServer(app);

const start = async () => {
  try {
    await ensureMinioBucket();
  } catch (error) {
    console.error('MinIO initialization failed', error);
    process.exit(1);
  }

  setupRealtimeGateway(server);
  startRecordingsWorker();
  startPushWorker();

  server.listen(port, () => {
    console.log(`Server is running on port ${port}`);
  });
};

start();
