/**
 * Tenant Subscription Controller
 * API endpoints for tenant subscription management
 *
 * @module BillingUsage
 * @prefix /api/v1/billing/subscription
 */

import { Router, Request, Response, NextFunction } from 'express';
import {
  TenantSubscriptionService,
  CreateTenantSubscriptionDto,
  UpdateTenantSubscriptionDto,
} from '../services/tenant-subscription.service';
import { BillingCalculationService } from '../services/billing-calculation.service';

const router = Router();
const subscriptionService = new TenantSubscriptionService();
const billingService = new BillingCalculationService();

/**
 * GET /api/v1/billing/subscription
 * Get current tenant's subscription
 */
router.get('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const subscription = await subscriptionService.findByTenantId(tenantId);
    if (!subscription) {
      return res.status(404).json({ error: 'No subscription found for this tenant' });
    }

    return res.json({ success: true, data: subscription });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/subscription/stats
 * Get subscription statistics (admin only)
 */
router.get('/stats', async (_req: Request, res: Response, next: NextFunction) => {
  try {
    const stats = await subscriptionService.getSubscriptionStats();

    return res.json({ success: true, data: stats });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/subscription/expiring
 * Get subscriptions expiring soon (admin only)
 */
router.get('/expiring', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const days = parseInt(req.query.days as string) || 30;
    const subscriptions = await subscriptionService.getExpiringSubscriptions(days);

    return res.json({
      success: true,
      data: subscriptions,
      count: subscriptions.length,
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/subscription/trials-ending
 * Get trials ending soon (admin only)
 */
router.get('/trials-ending', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const days = parseInt(req.query.days as string) || 7;
    const subscriptions = await subscriptionService.getTrialsEndingSoon(days);

    return res.json({
      success: true,
      data: subscriptions,
      count: subscriptions.length,
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/subscription/upcoming-charges
 * Get upcoming billing charges
 */
router.get('/upcoming-charges', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const charges = await billingService.getUpcomingCharges({ tenantId });
    if (!charges) {
      return res.status(404).json({ error: 'No subscription found' });
    }

    return res.json({ success: true, data: charges });
  } catch (error) {
    return next(error);
  }
});

/**
 * GET /api/v1/billing/subscription/limits
 * Check usage against limits
 */
router.get('/limits', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const limits = await billingService.checkAllLimits({ tenantId });

    return res.json({
      success: true,
      data: limits,
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/subscription
 * Create a subscription for tenant
 */
router.post('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    // Check if subscription already exists
    const existing = await subscriptionService.findByTenantId(tenantId);
    if (existing) {
      return res.status(409).json({ error: 'Subscription already exists for this tenant' });
    }

    const data: CreateTenantSubscriptionDto = req.body;

    if (!data.planId || !data.currentPeriodStart || !data.currentPeriodEnd || data.currentPrice === undefined) {
      return res.status(400).json({
        error: 'planId, currentPeriodStart, currentPeriodEnd, and currentPrice are required',
      });
    }

    const subscription = await subscriptionService.create({ tenantId }, data);
    return res.status(201).json({ success: true, data: subscription });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/subscription/trial
 * Start a trial subscription
 */
router.post('/trial', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { planId, trialDays = 14 } = req.body;

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

    // Check if subscription already exists
    const existing = await subscriptionService.findByTenantId(tenantId);
    if (existing) {
      return res.status(409).json({ error: 'Subscription already exists for this tenant' });
    }

    const subscription = await subscriptionService.startTrial({ tenantId }, planId, trialDays);
    return res.status(201).json({ success: true, data: subscription });
  } catch (error) {
    return next(error);
  }
});

/**
 * PATCH /api/v1/billing/subscription
 * Update subscription details
 */
router.patch('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const data: UpdateTenantSubscriptionDto = req.body;
    const subscription = await subscriptionService.update({ tenantId }, data);

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

    return res.json({ success: true, data: subscription });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/subscription/change-plan
 * Change subscription plan
 */
router.post('/change-plan', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { planId, effectiveDate } = req.body;

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

    const subscription = await subscriptionService.changePlan(
      { tenantId },
      planId,
      effectiveDate ? new Date(effectiveDate) : undefined
    );

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

    return res.json({ success: true, data: subscription });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/subscription/estimate-change
 * Estimate cost of plan change
 */
router.post('/estimate-change', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { planId } = req.body;

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

    const estimate = await billingService.estimatePlanChange({ tenantId }, planId);

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

    return res.json({ success: true, data: estimate });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/subscription/cancel
 * Cancel subscription
 */
router.post('/cancel', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { reason, immediately = false } = req.body;

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

    const subscription = await subscriptionService.cancel({ tenantId }, reason, immediately);

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

    return res.json({
      success: true,
      data: subscription,
      message: immediately
        ? 'Subscription cancelled immediately'
        : 'Subscription will cancel at end of period',
    });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/subscription/reactivate
 * Reactivate cancelled subscription
 */
router.post('/reactivate', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const subscription = await subscriptionService.reactivate({ tenantId });

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

    return res.json({ success: true, data: subscription });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/subscription/renew
 * Renew subscription
 */
router.post('/renew', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const subscription = await subscriptionService.renewSubscription({ tenantId });

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

    return res.json({ success: true, data: subscription });
  } catch (error) {
    return next(error);
  }
});

/**
 * POST /api/v1/billing/subscription/record-payment
 * Record a payment
 */
router.post('/record-payment', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tenantId = req.headers['x-tenant-id'] as string;
    if (!tenantId) {
      return res.status(400).json({ error: 'X-Tenant-Id header required' });
    }

    const { amount, paymentDate } = req.body;

    if (amount === undefined) {
      return res.status(400).json({ error: 'amount is required' });
    }

    const subscription = await subscriptionService.recordPayment(
      { tenantId },
      amount,
      paymentDate ? new Date(paymentDate) : undefined
    );

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

    return res.json({ success: true, data: subscription });
  } catch (error) {
    return next(error);
  }
});

export default router;
