import { Request, Response, NextFunction, Router } from 'express';
import { FeatureFlagsService } from '../services/feature-flags.service';

export class FeatureFlagsController {
  public router: Router;

  constructor(private readonly featureFlagsService: FeatureFlagsService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Flag CRUD
    this.router.get('/flags', this.findAllFlags.bind(this));
    this.router.get('/flags/all', this.findAllFlagsIncludingInactive.bind(this));
    this.router.get('/flags/tags/:tags', this.findFlagsByTags.bind(this));
    this.router.get('/flags/:id', this.findFlagById.bind(this));
    this.router.get('/flags/code/:code', this.findFlagByCode.bind(this));
    this.router.post('/flags', this.createFlag.bind(this));
    this.router.patch('/flags/:id', this.updateFlag.bind(this));
    this.router.delete('/flags/:id', this.deleteFlag.bind(this));
    this.router.patch('/flags/:id/toggle', this.toggleFlag.bind(this));
    this.router.get('/flags/:id/stats', this.getFlagStats.bind(this));

    // Tenant Overrides
    this.router.get('/flags/:flagId/overrides', this.findOverridesForFlag.bind(this));
    this.router.get('/tenants/:tenantId/overrides', this.findOverridesForTenant.bind(this));
    this.router.get('/overrides/:id', this.findOverrideById.bind(this));
    this.router.post('/overrides', this.createOverride.bind(this));
    this.router.patch('/overrides/:id', this.updateOverride.bind(this));
    this.router.delete('/overrides/:id', this.deleteOverride.bind(this));

    // Evaluation
    this.router.get('/evaluate/:code', this.evaluateFlag.bind(this));
    this.router.post('/evaluate', this.evaluateFlags.bind(this));
    this.router.get('/is-enabled/:code', this.isEnabled.bind(this));

    // Maintenance
    this.router.post('/maintenance/cleanup', this.cleanupExpiredOverrides.bind(this));
  }

  // ============================================
  // FLAGS
  // ============================================

  private async findAllFlags(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const flags = await this.featureFlagsService.findAllFlags();
      res.json({ data: flags, total: flags.length });
    } catch (error) {
      next(error);
    }
  }

  private async findAllFlagsIncludingInactive(
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const flags = await this.featureFlagsService.findAllFlagsIncludingInactive();
      res.json({ data: flags, total: flags.length });
    } catch (error) {
      next(error);
    }
  }

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

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

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

  private async findFlagByCode(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { code } = req.params;
      const flag = await this.featureFlagsService.findFlagByCode(code);

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

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

  private async findFlagsByTags(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { tags } = req.params;
      const tagList = tags.split(',');
      const flags = await this.featureFlagsService.findFlagsByTags(tagList);
      res.json({ data: flags, total: flags.length });
    } catch (error) {
      next(error);
    }
  }

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

  private async updateFlag(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const userId = req.headers['x-user-id'] as string;
      const flag = await this.featureFlagsService.updateFlag(id, req.body, userId);

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

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

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

      let result: boolean;
      if (soft === 'true') {
        const flag = await this.featureFlagsService.softDeleteFlag(id, userId);
        result = flag !== null;
      } else {
        result = await this.featureFlagsService.deleteFlag(id);
      }

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

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

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

      const flag = await this.featureFlagsService.toggleFlag(id, enabled, userId);

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

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

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

      if (!stats.flag) {
        res.status(404).json({ error: 'Flag not found' });
        return;
      }

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

  // ============================================
  // OVERRIDES
  // ============================================

  private async findOverridesForFlag(
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const { flagId } = req.params;
      const overrides = await this.featureFlagsService.findOverridesForFlag(flagId);
      res.json({ data: overrides, total: overrides.length });
    } catch (error) {
      next(error);
    }
  }

  private async findOverridesForTenant(
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const { tenantId } = req.params;
      const overrides = await this.featureFlagsService.findOverridesForTenant(tenantId);
      res.json({ data: overrides, total: overrides.length });
    } catch (error) {
      next(error);
    }
  }

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

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

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

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

  private async updateOverride(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const override = await this.featureFlagsService.updateOverride(id, req.body);

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

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

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

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

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // EVALUATION
  // ============================================

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

      if (!tenantId) {
        res.status(400).json({ error: 'tenantId is required (header x-tenant-id or query param)' });
        return;
      }

      const result = await this.featureFlagsService.evaluateFlag(code, tenantId);
      res.json({ data: result });
    } catch (error) {
      next(error);
    }
  }

  private async evaluateFlags(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { flagCodes, tenantId } = req.body;

      if (!flagCodes || !Array.isArray(flagCodes)) {
        res.status(400).json({ error: 'flagCodes array is required' });
        return;
      }

      if (!tenantId) {
        res.status(400).json({ error: 'tenantId is required' });
        return;
      }

      const results = await this.featureFlagsService.evaluateFlags(flagCodes, tenantId);
      res.json({ data: results, total: results.length });
    } catch (error) {
      next(error);
    }
  }

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

      if (!tenantId) {
        res.status(400).json({ error: 'tenantId is required (header x-tenant-id or query param)' });
        return;
      }

      const enabled = await this.featureFlagsService.isEnabled(code, tenantId);
      res.json({ data: { code, enabled } });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // MAINTENANCE
  // ============================================

  private async cleanupExpiredOverrides(
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const count = await this.featureFlagsService.cleanupExpiredOverrides();
      res.json({ data: { cleanedUp: count } });
    } catch (error) {
      next(error);
    }
  }
}
