/**
 * Billing controller for SentryAgent.ai AgentIdP.
 * Handles Stripe checkout session creation, webhook processing, and usage queries.
 */

import { Request, Response, NextFunction } from 'express';
import { BillingService } from '../services/BillingService.js';
import { UsageService } from '../services/UsageService.js';
import { ValidationError, AuthorizationError } from '../utils/errors.js';

/**
 * Controller for billing and usage endpoints.
 * Delegates all business logic to BillingService and UsageService.
 */
export class BillingController {
  /**
   * @param billingService - The billing service for Stripe operations.
   * @param usageService - The usage metering service.
   */
  constructor(
    private readonly billingService: BillingService,
    private readonly usageService: UsageService,
  ) {}

  /**
   * Handles POST /billing/checkout — creates a Stripe Checkout Session.
   * Reads the tenant ID from the authenticated user's organizationId.
   * Returns { checkoutUrl } with HTTP 201.
   *
   * @param req - Express request. Must have req.user populated.
   * @param res - Express response.
   * @param next - Express next function.
   */
  createCheckoutSession = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.user) {
        throw new AuthorizationError();
      }

      const tenantId = req.user.organization_id;
      if (!tenantId) {
        throw new ValidationError('organization_id is required in token.');
      }

      const body = req.body as { successUrl?: unknown; cancelUrl?: unknown };

      const successUrl =
        typeof body.successUrl === 'string' && body.successUrl.length > 0
          ? body.successUrl
          : `${req.protocol}://${req.hostname}/dashboard?billing=success`;

      const cancelUrl =
        typeof body.cancelUrl === 'string' && body.cancelUrl.length > 0
          ? body.cancelUrl
          : `${req.protocol}://${req.hostname}/dashboard?billing=cancel`;

      const checkoutUrl = await this.billingService.createCheckoutSession(
        tenantId,
        successUrl,
        cancelUrl,
      );

      res.status(201).json({ checkoutUrl });
    } catch (err) {
      next(err);
    }
  };

  /**
   * Handles POST /billing/webhook — processes Stripe webhook events.
   * Reads the raw body buffer and Stripe-Signature header.
   * Returns HTTP 200 { received: true } on success.
   * Returns HTTP 400 if the signature header is missing.
   *
   * @param req - Express request. Body must be a raw Buffer.
   * @param res - Express response.
   * @param next - Express next function.
   */
  handleWebhook = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const sig = req.headers['stripe-signature'];
      if (!sig || typeof sig !== 'string') {
        throw new ValidationError('Missing Stripe-Signature header.');
      }

      const webhookSecret = process.env['STRIPE_WEBHOOK_SECRET'];
      if (!webhookSecret) {
        throw new Error('STRIPE_WEBHOOK_SECRET environment variable is required.');
      }

      // req.body is a raw Buffer when express.raw() middleware is applied
      const rawBody = req.body as Buffer;

      await this.billingService.handleWebhookEvent(rawBody, sig, webhookSecret);

      res.status(200).json({ received: true });
    } catch (err) {
      next(err);
    }
  };

  /**
   * Handles GET /billing/usage — returns today's usage summary for the tenant.
   * Returns combined { tenantId, date, apiCalls, agentCount, subscriptionStatus }.
   *
   * @param req - Express request. Must have req.user populated.
   * @param res - Express response.
   * @param next - Express next function.
   */
  getUsage = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      if (!req.user) {
        throw new AuthorizationError();
      }

      const tenantId = req.user.organization_id;
      if (!tenantId) {
        throw new ValidationError('organization_id is required in token.');
      }

      const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD

      const [usage, subscription] = await Promise.all([
        this.usageService.getDailyUsage(tenantId, today),
        this.billingService.getSubscriptionStatus(tenantId),
      ]);

      res.status(200).json({
        tenantId: usage.tenantId,
        date: usage.date,
        apiCalls: usage.apiCalls,
        agentCount: usage.agentCount,
        subscriptionStatus: subscription.status,
        currentPeriodEnd: subscription.currentPeriodEnd,
        stripeSubscriptionId: subscription.stripeSubscriptionId,
      });
    } catch (err) {
      next(err);
    }
  };
}
