import { createServer } from "http";
import path from "path";
import { fileURLToPath } from "url";
import express, { type Express, type Request, type Response } from "express";

import { adminRoutes, adminWebSocket } from "./admin/index.js";
import { getConfig } from "./config.js";
import { createEndpointHandler } from "./middleware/index.js";
import { ALL_PROVIDERS } from "./providers/index.js";
import { registry } from "./registry.js";

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

const app: Express = express();
app.use(express.json());

const envConfig = getConfig();

// Initialize registry from environment config
registry.initFromEnv(envConfig);

// Register all providers
for (const provider of ALL_PROVIDERS) {
  registry.registerProvider(provider);
}

// =============================================================================
// Middleware
// =============================================================================

// Logging middleware (disabled by default since dashboard shows all requests)
const VERBOSE_LOGGING = process.env.VERBOSE === "true";
if (VERBOSE_LOGGING) {
  app.use((req, res, next) => {
    const start = Date.now();
    res.on("finish", () => {
      const duration = Date.now() - start;
      if (
        !req.path.startsWith("/api") &&
        !req.path.startsWith("/dashboard") &&
        req.path !== "/ws"
      ) {
        console.log(
          `${new Date().toISOString()} ${req.method} ${req.path} ${res.statusCode} ${duration}ms`
        );
      }
    });
    next();
  });
}

// =============================================================================
// Dashboard
// =============================================================================

// Simple rate limiter for dashboard routes (prevents abuse of file system access)
const dashboardRateLimiter = (() => {
  const requests = new Map<string, number[]>();
  const windowMs = 60000; // 1 minute
  const maxRequests = 100; // 100 requests per minute per IP

  return (req: Request, res: Response, next: () => void) => {
    const ip = req.ip || req.socket.remoteAddress || "unknown";
    const now = Date.now();
    const timestamps = requests.get(ip) || [];

    // Remove old timestamps outside the window
    const validTimestamps = timestamps.filter((t) => now - t < windowMs);

    if (validTimestamps.length >= maxRequests) {
      res.status(429).json({ error: "Too many requests" });
      return;
    }

    validTimestamps.push(now);
    requests.set(ip, validTimestamps);
    next();
  };
})();

// Serve static dashboard files in production
const dashboardPath = path.join(__dirname, "../dashboard/dist");
app.use("/dashboard", dashboardRateLimiter, express.static(dashboardPath));

// Serve index.html for all dashboard routes (SPA support)
app.get(
  "/dashboard/*splat",
  dashboardRateLimiter,
  (_req: Request, res: Response) => {
    res.sendFile(path.join(dashboardPath, "index.html"));
  }
);

// Redirect root /dashboard to /dashboard/
app.get("/dashboard", (_req: Request, res: Response) => {
  res.redirect("/dashboard/");
});

// =============================================================================
// Health & Models
// =============================================================================

app.get("/health", (_req: Request, res: Response) => {
  res.json({ status: "ok", timestamp: new Date().toISOString() });
});

app.get("/v1/models", (_req: Request, res: Response) => {
  res.json({
    object: "list",
    data: [
      {
        id: "gpt-4o",
        object: "model",
        created: 1715367049,
        owned_by: "mock-server",
      },
      {
        id: "gpt-4o-mini",
        object: "model",
        created: 1715367049,
        owned_by: "mock-server",
      },
      {
        id: "gpt-4-turbo",
        object: "model",
        created: 1715367049,
        owned_by: "mock-server",
      },
      {
        id: "gpt-3.5-turbo",
        object: "model",
        created: 1715367049,
        owned_by: "mock-server",
      },
    ],
  });
});

// =============================================================================
// Admin Routes
// =============================================================================

app.use("/api", adminRoutes);

// =============================================================================
// LLM Provider Endpoints
// =============================================================================

// Register all provider routes automatically
for (const provider of ALL_PROVIDERS) {
  const handler = createEndpointHandler(provider);

  if (provider.method === "POST") {
    app.post(provider.routePath, handler);
  } else if (provider.method === "GET") {
    app.get(provider.routePath, handler);
  }
}

// =============================================================================
// Server Lifecycle
// =============================================================================

let httpServer: ReturnType<typeof createServer> | null = null;

export function startServer(): Promise<void> {
  return new Promise((resolve) => {
    httpServer = createServer(app);

    // Attach WebSocket server
    adminWebSocket.attach(httpServer);

    httpServer.listen(envConfig.port, () => {
      console.log(`
🚀 Mock LLM Server running at http://localhost:${envConfig.port}

   Dashboard: http://localhost:${envConfig.port}/dashboard

   Endpoints:
     POST /v1/chat/completions      (OpenAI)
     POST /v1/responses             (OpenAI Responses API)
     POST /v1/messages              (Anthropic)
     POST /v1beta/models/*          (Gemini v1beta)
     POST /v1/models/*              (Gemini v1)

   Set VERBOSE=true for request logging
`);
      resolve();
    });
  });
}

export function stopServer(): Promise<void> {
  return new Promise((resolve) => {
    adminWebSocket.close();
    if (httpServer) {
      httpServer.close(() => {
        httpServer = null;
        resolve();
      });
    } else {
      resolve();
    }
  });
}

// Legacy exports for test compatibility
export function resetRateLimitState(): void {
  registry.resetAllRateLimiters();
}

export { app };

// Start server if run directly
const isMain =
  process.argv[1]?.endsWith("server.ts") ||
  process.argv[1]?.endsWith("server.js");
if (isMain) {
  startServer();
}
