import { Request, Response, NextFunction, Router } from 'express';
import { AuditService, AuditLogFilters } from '../services/audit.service';

export class AuditController {
  public router: Router;

  constructor(private readonly auditService: AuditService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Audit Logs
    this.router.get('/logs', this.findAuditLogs.bind(this));
    this.router.get('/logs/entity/:entityType/:entityId', this.findAuditLogsByEntity.bind(this));
    this.router.post('/logs', this.createAuditLog.bind(this));

    // Entity Changes
    this.router.get('/changes/:entityType/:entityId', this.findEntityChanges.bind(this));
    this.router.get('/changes/:entityType/:entityId/version/:version', this.getEntityVersion.bind(this));
    this.router.post('/changes', this.createEntityChange.bind(this));

    // Login History
    this.router.get('/logins/user/:userId', this.findLoginHistory.bind(this));
    this.router.get('/logins/user/:userId/active-sessions', this.getActiveSessionsCount.bind(this));
    this.router.post('/logins', this.createLoginHistory.bind(this));
    this.router.post('/logins/:sessionId/logout', this.markSessionLogout.bind(this));

    // Sensitive Data Access
    this.router.get('/sensitive-access', this.findSensitiveDataAccess.bind(this));
    this.router.post('/sensitive-access', this.logSensitiveDataAccess.bind(this));

    // Data Exports
    this.router.get('/exports', this.findUserDataExports.bind(this));
    this.router.get('/exports/:id', this.findDataExport.bind(this));
    this.router.post('/exports', this.createDataExport.bind(this));
    this.router.patch('/exports/:id/status', this.updateDataExportStatus.bind(this));

    // Permission Changes
    this.router.get('/permission-changes', this.findPermissionChanges.bind(this));
    this.router.post('/permission-changes', this.logPermissionChange.bind(this));

    // Config Changes
    this.router.get('/config-changes', this.findConfigChanges.bind(this));
    this.router.post('/config-changes', this.logConfigChange.bind(this));
  }

  // ============================================
  // AUDIT LOGS
  // ============================================

  private async findAuditLogs(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const filters: AuditLogFilters = {
        userId: req.query.userId as string,
        entityType: req.query.entityType as string,
        action: req.query.action as string,
        category: req.query.category as string,
        ipAddress: req.query.ipAddress as string,
      };

      if (req.query.startDate) filters.startDate = new Date(req.query.startDate as string);
      if (req.query.endDate) filters.endDate = new Date(req.query.endDate as string);

      const page = parseInt(req.query.page as string) || 1;
      const limit = parseInt(req.query.limit as string) || 50;

      const result = await this.auditService.findAuditLogs(tenantId, filters, { page, limit });
      res.json({ data: result.data, total: result.total, page, limit });
    } catch (error) {
      next(error);
    }
  }

  private async findAuditLogsByEntity(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const { entityType, entityId } = req.params;

      const logs = await this.auditService.findAuditLogsByEntity(tenantId, entityType, entityId);
      res.json({ data: logs, total: logs.length });
    } catch (error) {
      next(error);
    }
  }

  private async createAuditLog(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const log = await this.auditService.createAuditLog(tenantId, req.body);
      res.status(201).json({ data: log });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // ENTITY CHANGES
  // ============================================

  private async findEntityChanges(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const { entityType, entityId } = req.params;

      const changes = await this.auditService.findEntityChanges(tenantId, entityType, entityId);
      res.json({ data: changes, total: changes.length });
    } catch (error) {
      next(error);
    }
  }

  private async getEntityVersion(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const { entityType, entityId, version } = req.params;

      const change = await this.auditService.getEntityVersion(
        tenantId,
        entityType,
        entityId,
        parseInt(version)
      );

      if (!change) {
        res.status(404).json({ error: 'Version not found' });
        return;
      }

      res.json({ data: change });
    } catch (error) {
      next(error);
    }
  }

  private async createEntityChange(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const change = await this.auditService.createEntityChange(tenantId, req.body);
      res.status(201).json({ data: change });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // LOGIN HISTORY
  // ============================================

  private async findLoginHistory(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const { userId } = req.params;
      const limit = parseInt(req.query.limit as string) || 20;

      const history = await this.auditService.findLoginHistory(userId, tenantId, limit);
      res.json({ data: history, total: history.length });
    } catch (error) {
      next(error);
    }
  }

  private async getActiveSessionsCount(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { userId } = req.params;
      const count = await this.auditService.getActiveSessionsCount(userId);
      res.json({ data: { activeSessions: count } });
    } catch (error) {
      next(error);
    }
  }

  private async createLoginHistory(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const login = await this.auditService.createLoginHistory(req.body);
      res.status(201).json({ data: login });
    } catch (error) {
      next(error);
    }
  }

  private async markSessionLogout(_req: Request, res: Response, _next: NextFunction): Promise<void> {
    // Note: Session logout tracking requires a separate Session entity
    // LoginHistory only tracks login attempts, not active sessions
    res.status(501).json({
      error: 'Session logout tracking not implemented',
      message: 'Use the Auth module session endpoints for logout tracking',
    });
  }

  // ============================================
  // SENSITIVE DATA ACCESS
  // ============================================

  private async findSensitiveDataAccess(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const filters: { userId?: string; dataType?: string; startDate?: Date; endDate?: Date } = {
        userId: req.query.userId as string,
        dataType: req.query.dataType as string,
      };

      if (req.query.startDate) filters.startDate = new Date(req.query.startDate as string);
      if (req.query.endDate) filters.endDate = new Date(req.query.endDate as string);

      const access = await this.auditService.findSensitiveDataAccess(tenantId, filters);
      res.json({ data: access, total: access.length });
    } catch (error) {
      next(error);
    }
  }

  private async logSensitiveDataAccess(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const access = await this.auditService.logSensitiveDataAccess(tenantId, req.body);
      res.status(201).json({ data: access });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // DATA EXPORTS
  // ============================================

  private async findUserDataExports(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      const exports = await this.auditService.findUserDataExports(tenantId, userId);
      res.json({ data: exports, total: exports.length });
    } catch (error) {
      next(error);
    }
  }

  private async findDataExport(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const exportRecord = await this.auditService.findDataExport(id);

      if (!exportRecord) {
        res.status(404).json({ error: 'Export not found' });
        return;
      }

      res.json({ data: exportRecord });
    } catch (error) {
      next(error);
    }
  }

  private async createDataExport(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const exportRecord = await this.auditService.createDataExport(tenantId, req.body);
      res.status(201).json({ data: exportRecord });
    } catch (error) {
      next(error);
    }
  }

  private async updateDataExportStatus(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const { status, ...updates } = req.body;

      const exportRecord = await this.auditService.updateDataExportStatus(id, status, updates);

      if (!exportRecord) {
        res.status(404).json({ error: 'Export not found' });
        return;
      }

      res.json({ data: exportRecord });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // PERMISSION CHANGES
  // ============================================

  private async findPermissionChanges(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const targetUserId = req.query.targetUserId as string;

      const changes = await this.auditService.findPermissionChanges(tenantId, targetUserId);
      res.json({ data: changes, total: changes.length });
    } catch (error) {
      next(error);
    }
  }

  private async logPermissionChange(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const change = await this.auditService.logPermissionChange(tenantId, req.body);
      res.status(201).json({ data: change });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // CONFIG CHANGES
  // ============================================

  private async findConfigChanges(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const configType = req.query.configType as string;

      const changes = await this.auditService.findConfigChanges(tenantId, configType);
      res.json({ data: changes, total: changes.length });
    } catch (error) {
      next(error);
    }
  }

  private async logConfigChange(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const change = await this.auditService.logConfigChange(tenantId, req.body);
      res.status(201).json({ data: change });
    } catch (error) {
      next(error);
    }
  }
}
