import * as plugins from '../../plugins.js';
import type { OpsServer } from '../classes.opsserver.js';
import * as interfaces from '../../../ts_interfaces/index.js';
import { requireOpsAuth } from '../helpers/auth.js';

export class SecurityHandler {
  constructor(private opsServerRef: OpsServer) {
    this.registerHandlers();
  }

  private registerHandlers(): void {
    // All security endpoints register directly on viewRouter (valid identity required via middleware)
    const router = this.opsServerRef.viewRouter;

    // Security Metrics Handler
    router.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSecurityMetrics>(
        'getSecurityMetrics',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
          const metrics = await this.collectSecurityMetrics();
          return {
            metrics: {
              blockedIPs: metrics.blockedIPs,
              reputationScores: metrics.reputationScores,
              spamDetected: metrics.spamDetection.detected,
              malwareDetected: metrics.malwareDetected,
              phishingDetected: metrics.phishingDetected,
              authenticationFailures: metrics.authFailures,
              suspiciousActivities: metrics.suspiciousActivities,
            },
            trends: dataArg.includeDetails ? {
              spam: metrics.trends.spam,
              malware: metrics.trends.malware,
              phishing: metrics.trends.phishing,
            } : undefined,
          };
        }
      )
    );
    
    // Active Connections Handler
    router.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetActiveConnections>(
        'getActiveConnections',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
          const connectionInfos = await this.getActiveConnections(dataArg.protocol, dataArg.state);
          const totalConnections = connectionInfos.reduce((sum, conn) => sum + (conn.connectionCount || 1), 0);
          
          const summary = {
            total: totalConnections,
            byProtocol: connectionInfos.reduce((acc, conn) => {
              acc[conn.protocol] = (acc[conn.protocol] || 0) + (conn.connectionCount || 1);
              return acc;
            }, {} as { [protocol: string]: number }),
            byState: connectionInfos.reduce((acc, conn) => {
              acc[conn.state] = (acc[conn.state] || 0) + (conn.connectionCount || 1);
              return acc;
            }, {} as { [state: string]: number }),
          };
          
          return {
            connections: connectionInfos,
            summary,
          };
        }
      )
    );
    
    // Network Stats Handler - provides comprehensive network metrics
    router.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetNetworkStats>(
        'getNetworkStats',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
          // Get network stats from MetricsManager if available
          if (this.opsServerRef.dcRouterRef.metricsManager) {
            const networkStats = await this.opsServerRef.dcRouterRef.metricsManager.getNetworkStats();

            // Convert per-IP throughput Map to serializable array
            const throughputByIP: Array<{ ip: string; in: number; out: number }> = [];
            if (networkStats.throughputByIP) {
              for (const [ip, tp] of networkStats.throughputByIP) {
                throughputByIP.push({ ip, in: tp.in, out: tp.out });
              }
            }

            return {
              connectionsByIP: Array.from(networkStats.connectionsByIP.entries()).map(([ip, count]) => ({ ip, count })),
              throughputRate: networkStats.throughputRate,
              topIPs: networkStats.topIPs,
              topIPsByBandwidth: networkStats.topIPsByBandwidth,
              topASNs: networkStats.topASNs,
              totalDataTransferred: networkStats.totalDataTransferred,
              throughputHistory: networkStats.throughputHistory || [],
              throughputByIP,
              domainActivity: networkStats.domainActivity || [],
              requestsPerSecond: networkStats.requestsPerSecond || 0,
              requestsTotal: networkStats.requestsTotal || 0,
              backends: networkStats.backends || [],
              frontendProtocols: networkStats.frontendProtocols || null,
              backendProtocols: networkStats.backendProtocols || null,
            };
          }

          // Fallback if MetricsManager not available
          return {
            connectionsByIP: [],
            throughputRate: { bytesInPerSecond: 0, bytesOutPerSecond: 0 },
            topIPs: [],
            topIPsByBandwidth: [],
            topASNs: [],
            totalDataTransferred: { bytesIn: 0, bytesOut: 0 },
            throughputHistory: [],
            throughputByIP: [],
            domainActivity: [],
            requestsPerSecond: 0,
            requestsTotal: 0,
            backends: [],
            frontendProtocols: null,
            backendProtocols: null,
          };
        }
      )
    );
    
    // Rate Limit Status Handler
    router.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRateLimitStatus>(
        'getRateLimitStatus',
        async (dataArg, toolsArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
          const status = await this.getRateLimitStatus(dataArg.domain, dataArg.ip);
          const limits: interfaces.data.IRateLimitInfo[] = status.limits.map(limit => ({
            domain: limit.identifier,
            currentRate: limit.current,
            limit: limit.limit,
            remaining: limit.limit - limit.current,
            resetTime: limit.resetAt,
            blocked: limit.status === 'limited',
          }));
          
          return {
            limits,
            globalLimit: dataArg.includeBlocked ? {
              current: limits.reduce((sum, l) => sum + l.currentRate, 0),
              limit: 1000, // Global limit
              remaining: 1000 - limits.reduce((sum, l) => sum + l.currentRate, 0),
            } : undefined,
          };
        }
      )
    );

    router.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListSecurityBlockRules>(
        'listSecurityBlockRules',
        async (dataArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
          const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
          return { rules: manager ? await manager.listBlockRules() : [] };
        },
      ),
    );

    router.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListIpIntelligence>(
        'listIpIntelligence',
        async (dataArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
          const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
          return {
            records: manager
              ? await manager.listIpIntelligence({
                  ipAddresses: dataArg.ipAddresses,
                  limit: dataArg.limit,
                })
              : [],
          };
        },
      ),
    );

    router.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetCompiledSecurityPolicy>(
        'getCompiledSecurityPolicy',
        async (dataArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
          const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
          return {
            policy: manager
              ? await manager.compilePolicy()
              : { blockedIps: [], blockedCidrs: [] },
          };
        },
      ),
    );

    router.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListSecurityPolicyAudit>(
        'listSecurityPolicyAudit',
        async (dataArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
          const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
          return { events: manager ? await manager.listAuditEvents(dataArg.limit || 100) : [] };
        },
      ),
    );

    const adminRouter = this.opsServerRef.adminRouter;

    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateSecurityBlockRule>(
        'createSecurityBlockRule',
        async (dataArg) => {
          const auth = await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'security:write',
            requireAdminIdentity: true,
          });
          const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
          if (!manager) return { success: false, message: 'Security policy manager not initialized' };
          const rule = await manager.createBlockRule({
            type: dataArg.type,
            value: dataArg.value,
            matchMode: dataArg.matchMode,
            reason: dataArg.reason,
            enabled: dataArg.enabled,
          }, auth.userId);
          return { success: true, rule };
        },
      ),
    );

    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateSecurityBlockRule>(
        'updateSecurityBlockRule',
        async (dataArg) => {
          const auth = await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'security:write',
            requireAdminIdentity: true,
          });
          const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
          if (!manager) return { success: false, message: 'Security policy manager not initialized' };
          const rule = await manager.updateBlockRule(dataArg.id, {
            value: dataArg.value,
            matchMode: dataArg.matchMode,
            reason: dataArg.reason,
            enabled: dataArg.enabled,
          }, auth.userId);
          return rule ? { success: true, rule } : { success: false, message: 'Rule not found' };
        },
      ),
    );

    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteSecurityBlockRule>(
        'deleteSecurityBlockRule',
        async (dataArg) => {
          const auth = await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'security:write',
            requireAdminIdentity: true,
          });
          const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
          if (!manager) return { success: false, message: 'Security policy manager not initialized' };
          const success = await manager.deleteBlockRule(dataArg.id, auth.userId);
          return { success, message: success ? undefined : 'Rule not found' };
        },
      ),
    );

    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RefreshIpIntelligence>(
        'refreshIpIntelligence',
        async (dataArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'security:write',
            requireAdminIdentity: true,
          });
          const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
          if (!manager) return { success: false, message: 'Security policy manager not initialized' };
          const record = await manager.refreshIpIntelligence(dataArg.ipAddress);
          return record
            ? { success: true, record }
            : { success: false, message: 'IP address is invalid or not public' };
        },
      ),
    );
  }
  
  private async collectSecurityMetrics(): Promise<{
    blockedIPs: string[];
    reputationScores: { [domain: string]: number };
    spamDetection: {
      detected: number;
      falsePositives: number;
    };
    malwareDetected: number;
    phishingDetected: number;
    authFailures: number;
    suspiciousActivities: number;
    trends: {
      spam: Array<{ timestamp: number; value: number }>;
      malware: Array<{ timestamp: number; value: number }>;
      phishing: Array<{ timestamp: number; value: number }>;
    };
  }> {
    // Get metrics from MetricsManager if available
    if (this.opsServerRef.dcRouterRef.metricsManager) {
      const securityStats = await this.opsServerRef.dcRouterRef.metricsManager.getSecurityStats();
      return {
        blockedIPs: [], // TODO: Track actual blocked IPs
        reputationScores: {},
        spamDetection: {
          detected: securityStats.spamDetected,
          falsePositives: 0,
        },
        malwareDetected: securityStats.malwareDetected,
        phishingDetected: securityStats.phishingDetected,
        authFailures: securityStats.authFailures,
        suspiciousActivities: 0,
        trends: {
          spam: [],
          malware: [],
          phishing: [],
        },
      };
    }
    
    // Fallback if MetricsManager not available
    return {
      blockedIPs: [],
      reputationScores: {},
      spamDetection: {
        detected: 0,
        falsePositives: 0,
      },
      malwareDetected: 0,
      phishingDetected: 0,
      authFailures: 0,
      suspiciousActivities: 0,
      trends: {
        spam: [],
        malware: [],
        phishing: [],
      },
    };
  }
  
  private async getActiveConnections(
    protocol?: 'http' | 'https' | 'smtp' | 'smtps',
    state?: string
  ): Promise<interfaces.data.IConnectionInfo[]> {
    const metricsManager = this.opsServerRef.dcRouterRef.metricsManager;
    if (!metricsManager) {
      return [];
    }

    const snapshots = await metricsManager.getActiveConnectionSnapshots({ limit: 10000 });
    const connections = snapshots.map((snapshot): interfaces.data.IConnectionInfo => ({
      id: String(snapshot.id),
      remoteAddress: snapshot.sourcePort === null
        ? snapshot.sourceIp
        : `${snapshot.sourceIp}:${snapshot.sourcePort}`,
      localAddress: snapshot.targetHost
        ? `${snapshot.targetHost}:${snapshot.targetPort ?? snapshot.localPort}`
        : `${this.opsServerRef.dcRouterRef.options.publicIp || 'server'}:${snapshot.localPort}`,
      startTime: snapshot.startedAtMs,
      protocol: this.mapSnapshotProtocol(snapshot),
      state: this.mapSnapshotState(snapshot.state),
      bytesReceived: snapshot.bytesIn,
      bytesSent: snapshot.bytesOut,
    }));

    return connections.filter((connection) => {
      if (protocol && connection.protocol !== protocol) {
        return false;
      }
      if (state && connection.state !== state) {
        return false;
      }
      return true;
    });
  }

  private mapSnapshotProtocol(
    snapshot: plugins.smartproxy.IActiveConnectionSnapshot,
  ): interfaces.data.IConnectionInfo['protocol'] {
    if (snapshot.localPort === 465) {
      return 'smtps';
    }
    if ([25, 587, 2525].includes(snapshot.localPort)) {
      return 'smtp';
    }

    switch (snapshot.protocol) {
      case 'http':
        return 'http';
      case 'https':
      case 'tls':
      case 'tls-passthrough':
      case 'tls-reencrypt':
      case 'tls-socket-handler':
      case 'quic':
        return 'https';
      default:
        return snapshot.localPort === 80 ? 'http' : 'https';
    }
  }

  private mapSnapshotState(state: string): interfaces.data.IConnectionInfo['state'] {
    return state === 'closing' ? 'closing' : 'connected';
  }
  
  private async getRateLimitStatus(
    domain?: string,
    ip?: string
  ): Promise<{
    limits: Array<{
      identifier: string;
      type: 'ip' | 'domain' | 'email';
      limit: number;
      current: number;
      resetAt: number;
      status: 'ok' | 'warning' | 'limited';
    }>;
  }> {
    // TODO: Implement actual rate limit status collection
    return {
      limits: [],
    };
  }
}
