import type { Express } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import { insertRouterSchema, insertDetectionSchema, insertWhitelistSchema } from "@shared/schema";

export async function registerRoutes(app: Express): Promise<Server> {
  // Routers
  app.get("/api/routers", async (req, res) => {
    try {
      const routers = await storage.getAllRouters();
      res.json(routers);
    } catch (error) {
      console.error('[DB ERROR] Failed to fetch routers:', error);
      res.status(500).json({ error: "Failed to fetch routers" });
    }
  });

  app.post("/api/routers", async (req, res) => {
    try {
      const validatedData = insertRouterSchema.parse(req.body);
      const router = await storage.createRouter(validatedData);
      res.json(router);
    } catch (error) {
      res.status(400).json({ error: "Invalid router data" });
    }
  });

  app.delete("/api/routers/:id", async (req, res) => {
    try {
      const success = await storage.deleteRouter(req.params.id);
      if (!success) {
        return res.status(404).json({ error: "Router not found" });
      }
      res.json({ success: true });
    } catch (error) {
      res.status(500).json({ error: "Failed to delete router" });
    }
  });

  // Network Logs
  app.get("/api/logs", async (req, res) => {
    try {
      const limit = parseInt(req.query.limit as string) || 100;
      const logs = await storage.getRecentLogs(limit);
      res.json(logs);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch logs" });
    }
  });

  app.get("/api/logs/ip/:ip", async (req, res) => {
    try {
      const limit = parseInt(req.query.limit as string) || 50;
      const logs = await storage.getLogsByIp(req.params.ip, limit);
      res.json(logs);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch logs for IP" });
    }
  });

  // Detections
  app.get("/api/detections", async (req, res) => {
    try {
      const limit = parseInt(req.query.limit as string) || 100;
      const detections = await storage.getAllDetections(limit);
      res.json(detections);
    } catch (error) {
      console.error('[DB ERROR] Failed to fetch detections:', error);
      res.status(500).json({ error: "Failed to fetch detections" });
    }
  });

  app.get("/api/detections/unblocked", async (req, res) => {
    try {
      const detections = await storage.getUnblockedDetections();
      res.json(detections);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch unblocked detections" });
    }
  });

  // Whitelist
  app.get("/api/whitelist", async (req, res) => {
    try {
      const whitelist = await storage.getAllWhitelist();
      res.json(whitelist);
    } catch (error) {
      console.error('[DB ERROR] Failed to fetch whitelist:', error);
      res.status(500).json({ error: "Failed to fetch whitelist" });
    }
  });

  app.post("/api/whitelist", async (req, res) => {
    try {
      const validatedData = insertWhitelistSchema.parse(req.body);
      const item = await storage.createWhitelist(validatedData);
      res.json(item);
    } catch (error) {
      res.status(400).json({ error: "Invalid whitelist data" });
    }
  });

  app.delete("/api/whitelist/:id", async (req, res) => {
    try {
      const success = await storage.deleteWhitelist(req.params.id);
      if (!success) {
        return res.status(404).json({ error: "Whitelist entry not found" });
      }
      res.json({ success: true });
    } catch (error) {
      res.status(500).json({ error: "Failed to delete whitelist entry" });
    }
  });

  // Training History
  app.get("/api/training-history", async (req, res) => {
    try {
      const limit = parseInt(req.query.limit as string) || 10;
      const history = await storage.getTrainingHistory(limit);
      res.json(history);
    } catch (error) {
      console.error('[DB ERROR] Failed to fetch training history:', error);
      res.status(500).json({ error: "Failed to fetch training history" });
    }
  });

  app.get("/api/training-history/latest", async (req, res) => {
    try {
      const latest = await storage.getLatestTraining();
      res.json(latest || null);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch latest training" });
    }
  });

  // Network Analytics
  app.get("/api/analytics/recent", async (req, res) => {
    try {
      const days = parseInt(req.query.days as string) || 3;
      const hourly = req.query.hourly === 'true';
      const analytics = await storage.getRecentAnalytics(days, hourly);
      res.json(analytics);
    } catch (error) {
      console.error('[DB ERROR] Failed to fetch recent analytics:', error);
      res.status(500).json({ error: "Failed to fetch analytics" });
    }
  });

  app.get("/api/analytics/range", async (req, res) => {
    try {
      const startDate = new Date(req.query.start as string);
      const endDate = new Date(req.query.end as string);
      const hourly = req.query.hourly === 'true';
      
      if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
        return res.status(400).json({ error: "Invalid date range" });
      }
      
      const analytics = await storage.getAnalyticsByDateRange(startDate, endDate, hourly);
      res.json(analytics);
    } catch (error) {
      console.error('[DB ERROR] Failed to fetch analytics range:', error);
      res.status(500).json({ error: "Failed to fetch analytics" });
    }
  });

  // Stats
  app.get("/api/stats", async (req, res) => {
    try {
      const routers = await storage.getAllRouters();
      const detections = await storage.getAllDetections(1000);
      const recentLogs = await storage.getRecentLogs(1000);
      const whitelist = await storage.getAllWhitelist();
      const latestTraining = await storage.getLatestTraining();

      const blockedCount = detections.filter(d => d.blocked).length;
      const criticalCount = detections.filter(d => parseFloat(d.riskScore) >= 85).length;
      const highCount = detections.filter(d => parseFloat(d.riskScore) >= 70 && parseFloat(d.riskScore) < 85).length;

      res.json({
        routers: {
          total: routers.length,
          enabled: routers.filter(r => r.enabled).length
        },
        detections: {
          total: detections.length,
          blocked: blockedCount,
          critical: criticalCount,
          high: highCount
        },
        logs: {
          recent: recentLogs.length
        },
        whitelist: {
          total: whitelist.length
        },
        latestTraining: latestTraining
      });
    } catch (error) {
      console.error('[DB ERROR] Failed to fetch stats:', error);
      res.status(500).json({ error: "Failed to fetch stats" });
    }
  });

  // ML Actions - Trigger training/detection on Python backend
  const ML_BACKEND_URL = process.env.ML_BACKEND_URL || "http://localhost:8000";
  const ML_TIMEOUT = 120000; // 2 minutes timeout
  const IDS_API_KEY = process.env.IDS_API_KEY; // API Key for secure ML backend communication
  
  // Helper to create authenticated fetch headers
  const getMLBackendHeaders = () => {
    const headers: HeadersInit = {
      "Content-Type": "application/json",
    };
    if (IDS_API_KEY) {
      headers["X-API-Key"] = IDS_API_KEY;
    }
    return headers;
  };

  app.post("/api/ml/train", async (req, res) => {
    try {
      const { max_records = 100000, hours_back = 24 } = req.body;
      
      // Validate input
      if (typeof max_records !== 'number' || max_records <= 0 || max_records > 1000000) {
        return res.status(400).json({ error: "max_records must be between 1 and 1000000" });
      }
      if (typeof hours_back !== 'number' || hours_back <= 0 || hours_back > 720) {
        return res.status(400).json({ error: "hours_back must be between 1 and 720" });
      }

      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), ML_TIMEOUT);

      const response = await fetch(`${ML_BACKEND_URL}/train`, {
        method: "POST",
        headers: getMLBackendHeaders(),
        body: JSON.stringify({ max_records, hours_back }),
        signal: controller.signal,
      });

      clearTimeout(timeout);

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        return res.status(response.status).json({ 
          error: errorData.detail || "Training failed",
          status: response.status,
        });
      }

      const data = await response.json();
      res.json(data);
    } catch (error: any) {
      if (error.name === 'AbortError') {
        return res.status(504).json({ error: "Training timeout - operation took too long" });
      }
      if (error.code === 'ECONNREFUSED') {
        return res.status(503).json({ error: "ML backend not available - is Python server running?" });
      }
      res.status(500).json({ error: error.message || "Failed to trigger training" });
    }
  });

  app.post("/api/ml/detect", async (req, res) => {
    try {
      const { max_records = 50000, hours_back = 1, risk_threshold = 75, auto_block = false } = req.body;
      
      // Validate input
      if (typeof max_records !== 'number' || max_records <= 0 || max_records > 1000000) {
        return res.status(400).json({ error: "max_records must be between 1 and 1000000" });
      }
      if (typeof hours_back !== 'number' || hours_back <= 0 || hours_back > 720) {
        return res.status(400).json({ error: "hours_back must be between 1 and 720" });
      }
      if (typeof risk_threshold !== 'number' || risk_threshold < 0 || risk_threshold > 100) {
        return res.status(400).json({ error: "risk_threshold must be between 0 and 100" });
      }

      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), ML_TIMEOUT);

      const response = await fetch(`${ML_BACKEND_URL}/detect`, {
        method: "POST",
        headers: getMLBackendHeaders(),
        body: JSON.stringify({ max_records, hours_back, risk_threshold, auto_block }),
        signal: controller.signal,
      });

      clearTimeout(timeout);

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        return res.status(response.status).json({ 
          error: errorData.detail || "Detection failed",
          status: response.status,
        });
      }

      const data = await response.json();
      res.json(data);
    } catch (error: any) {
      if (error.name === 'AbortError') {
        return res.status(504).json({ error: "Detection timeout - operation took too long" });
      }
      if (error.code === 'ECONNREFUSED') {
        return res.status(503).json({ error: "ML backend not available - is Python server running?" });
      }
      res.status(500).json({ error: error.message || "Failed to trigger detection" });
    }
  });

  app.get("/api/ml/stats", async (req, res) => {
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 10000); // 10s timeout for stats

      const response = await fetch(`${ML_BACKEND_URL}/stats`, {
        headers: getMLBackendHeaders(),
        signal: controller.signal,
      });

      clearTimeout(timeout);

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        return res.status(response.status).json({ 
          error: errorData.detail || "Failed to fetch ML stats",
          status: response.status,
        });
      }

      const data = await response.json();
      res.json(data);
    } catch (error: any) {
      if (error.name === 'AbortError') {
        return res.status(504).json({ error: "Stats timeout" });
      }
      if (error.code === 'ECONNREFUSED') {
        return res.status(503).json({ error: "ML backend not available" });
      }
      res.status(500).json({ error: error.message || "Failed to fetch ML stats" });
    }
  });

  // Services monitoring
  app.get("/api/services/status", async (req, res) => {
    try {
      const services = {
        mlBackend: { name: "ML Backend Python", status: "unknown", healthy: false, details: null as any },
        database: { name: "PostgreSQL Database", status: "unknown", healthy: false, details: null as any },
        syslogParser: { name: "Syslog Parser", status: "unknown", healthy: false, details: null as any },
      };

      // Check ML Backend Python
      try {
        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), 5000);
        
        const response = await fetch(`${ML_BACKEND_URL}/health`, {
          signal: controller.signal,
        });
        
        clearTimeout(timeout);

        if (response.ok) {
          const data = await response.json();
          services.mlBackend.status = "running";
          services.mlBackend.healthy = true;
          services.mlBackend.details = {
            modelLoaded: data.ml_model === "loaded",
            timestamp: data.timestamp,
          };
        } else {
          services.mlBackend.status = "error";
          services.mlBackend.details = { error: `HTTP ${response.status}` };
        }
      } catch (error: any) {
        services.mlBackend.status = "offline";
        services.mlBackend.details = { error: error.code === 'ECONNREFUSED' ? "Connection refused" : error.message };
      }

      // Check Database
      try {
        const conn = await storage.testConnection();
        if (conn) {
          services.database.status = "running";
          services.database.healthy = true;
          services.database.details = { connected: true };
        }
      } catch (error: any) {
        services.database.status = "error";
        services.database.details = { error: error.message };
      }

      // Check Python Services via authenticated endpoint
      try {
        const controller2 = new AbortController();
        const timeout2 = setTimeout(() => controller2.abort(), 5000);
        
        const servicesResponse = await fetch(`${ML_BACKEND_URL}/services/status`, {
          headers: getMLBackendHeaders(),
          signal: controller2.signal,
        });
        
        clearTimeout(timeout2);
        
        if (servicesResponse.ok) {
          const servicesData = await servicesResponse.json();
          
          // Update syslog parser status
          const parserInfo = servicesData.services?.syslog_parser;
          if (parserInfo) {
            services.syslogParser.status = parserInfo.running ? "running" : "offline";
            services.syslogParser.healthy = parserInfo.running;
            services.syslogParser.details = {
              systemd_unit: parserInfo.systemd_unit,
              pid: parserInfo.details?.pid,
              error: parserInfo.error,
            };
          }
        } else if (servicesResponse.status === 403) {
          services.syslogParser.status = "error";
          services.syslogParser.healthy = false;
          services.syslogParser.details = { error: "Authentication failed" };
        } else {
          throw new Error(`HTTP ${servicesResponse.status}`);
        }
      } catch (error: any) {
        services.syslogParser.status = "error";
        services.syslogParser.healthy = false;
        services.syslogParser.details = { 
          error: error.code === 'ECONNREFUSED' ? "ML Backend offline" : error.message 
        };
      }

      res.json({ services });
    } catch (error: any) {
      res.status(500).json({ error: "Failed to check services status" });
    }
  });

  // Service Control Endpoints (Secured - only allow specific systemd operations)
  const ALLOWED_SERVICES = ["ids-ml-backend", "ids-syslog-parser"];
  const ALLOWED_ACTIONS = ["start", "stop", "restart", "status"];

  app.post("/api/services/:service/:action", async (req, res) => {
    try {
      const { service, action } = req.params;

      // Validate service name
      if (!ALLOWED_SERVICES.includes(service)) {
        return res.status(400).json({ error: "Invalid service name" });
      }

      // Validate action
      if (!ALLOWED_ACTIONS.includes(action)) {
        return res.status(400).json({ error: "Invalid action" });
      }

      // Execute systemd command
      const { exec } = await import("child_process");
      const { promisify } = await import("util");
      const execAsync = promisify(exec);

      try {
        const systemdAction = action === "status" ? "status" : action;
        const { stdout, stderr } = await execAsync(
          `systemctl ${systemdAction} ${service}`,
          { timeout: 10000 }
        );

        res.json({
          success: true,
          service,
          action,
          output: stdout || stderr,
          timestamp: new Date().toISOString(),
        });
      } catch (execError: any) {
        // systemctl returns non-zero exit for stopped services in status command
        if (action === "status") {
          res.json({
            success: true,
            service,
            action,
            output: execError.stdout || execError.stderr,
            timestamp: new Date().toISOString(),
          });
        } else {
          throw execError;
        }
      }
    } catch (error: any) {
      res.status(500).json({
        error: "Service control failed",
        details: error.message,
      });
    }
  });

  const httpServer = createServer(app);
  return httpServer;
}
