/**
 * Subscriptions Controller
 *
 * REST API endpoints for tenant subscription management
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import { SubscriptionsService } from '../services';
import {
  CreateTenantSubscriptionDto,
  UpdateTenantSubscriptionDto,
  CancelSubscriptionDto,
  ChangePlanDto,
  SetPaymentMethodDto,
} from '../dto';

export class SubscriptionsController {
  public router: Router;
  private service: SubscriptionsService;

  constructor(dataSource: DataSource) {
    this.router = Router();
    this.service = new SubscriptionsService(dataSource);
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Stats (admin)
    this.router.get('/stats', this.getStats.bind(this));

    // Tenant subscription
    this.router.get('/tenant/:tenantId', this.getByTenant.bind(this));
    this.router.post('/', this.create.bind(this));
    this.router.put('/:id', this.update.bind(this));

    // Subscription actions
    this.router.post('/:id/cancel', this.cancel.bind(this));
    this.router.post('/:id/reactivate', this.reactivate.bind(this));
    this.router.post('/:id/change-plan', this.changePlan.bind(this));
    this.router.post('/:id/payment-method', this.setPaymentMethod.bind(this));
    this.router.post('/:id/renew', this.renew.bind(this));
    this.router.post('/:id/suspend', this.suspend.bind(this));
    this.router.post('/:id/activate', this.activate.bind(this));

    // Alerts/expiring
    this.router.get('/expiring', this.getExpiring.bind(this));
    this.router.get('/trials-ending', this.getTrialsEnding.bind(this));
  }

  /**
   * GET /subscriptions/stats
   * Get subscription statistics
   */
  private async getStats(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const stats = await this.service.getStats();
      res.json({ data: stats });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /subscriptions/tenant/:tenantId
   * Get subscription by tenant ID
   */
  private async getByTenant(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const subscription = await this.service.findByTenantId(req.params.tenantId);

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

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

  /**
   * POST /subscriptions
   * Create new subscription
   */
  private async create(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: CreateTenantSubscriptionDto = req.body;
      const subscription = await this.service.create(dto);
      res.status(201).json({ data: subscription });
    } catch (error) {
      next(error);
    }
  }

  /**
   * PUT /subscriptions/:id
   * Update subscription
   */
  private async update(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: UpdateTenantSubscriptionDto = req.body;
      const subscription = await this.service.update(req.params.id, dto);
      res.json({ data: subscription });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /subscriptions/:id/cancel
   * Cancel subscription
   */
  private async cancel(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: CancelSubscriptionDto = req.body;
      const subscription = await this.service.cancel(req.params.id, dto);
      res.json({ data: subscription });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /subscriptions/:id/reactivate
   * Reactivate cancelled subscription
   */
  private async reactivate(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const subscription = await this.service.reactivate(req.params.id);
      res.json({ data: subscription });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /subscriptions/:id/change-plan
   * Change subscription plan
   */
  private async changePlan(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: ChangePlanDto = req.body;
      const subscription = await this.service.changePlan(req.params.id, dto);
      res.json({ data: subscription });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /subscriptions/:id/payment-method
   * Set payment method
   */
  private async setPaymentMethod(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: SetPaymentMethodDto = req.body;
      const subscription = await this.service.setPaymentMethod(req.params.id, dto);
      res.json({ data: subscription });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /subscriptions/:id/renew
   * Renew subscription
   */
  private async renew(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const subscription = await this.service.renew(req.params.id);
      res.json({ data: subscription });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /subscriptions/:id/suspend
   * Suspend subscription
   */
  private async suspend(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const subscription = await this.service.suspend(req.params.id);
      res.json({ data: subscription });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /subscriptions/:id/activate
   * Activate subscription
   */
  private async activate(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const subscription = await this.service.activate(req.params.id);
      res.json({ data: subscription });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /subscriptions/expiring
   * Get subscriptions expiring soon
   */
  private async getExpiring(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const days = parseInt(req.query.days as string) || 7;
      const subscriptions = await this.service.findExpiringSoon(days);
      res.json({ data: subscriptions });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /subscriptions/trials-ending
   * Get trials ending soon
   */
  private async getTrialsEnding(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const days = parseInt(req.query.days as string) || 3;
      const subscriptions = await this.service.findTrialsEndingSoon(days);
      res.json({ data: subscriptions });
    } catch (error) {
      next(error);
    }
  }
}
