import {
  Controller,
  Get,
  Post,
  Body,
  Req,
  Res,
  HttpCode,
  HttpStatus,
  Logger,
  ForbiddenException,
} from "@nestjs/common";
import type { RawBodyRequest } from "@nestjs/common";
import {
  ApiTags,
  ApiBearerAuth,
  ApiOperation,
  ApiOkResponse,
} from "@nestjs/swagger";
import type { Request, Response } from "express";
import { CurrentUser, Public } from "../../common/decorators";
import type { ApiResponse } from "../../common/types/api-response.type";
import {
  createSuccessResponse,
  createErrorResponse,
} from "../../common/types/api-response.type";
import { SubscriptionsService } from "./subscriptions.service";
import { PaddleService, PaddleWebhookEvent } from "./paddle.service";
import {
  CreateCheckoutDto,
  CancelSubscriptionDto,
  SubscriptionResponseDto,
  PlanInfo,
  PlanType,
} from "./dto/subscription.dto";

interface AuthenticatedUser {
  id: string;
  email: string;
  role: string;
}

@ApiTags("Subscriptions")
@Controller("subscriptions")
export class SubscriptionsController {
  private readonly logger = new Logger(SubscriptionsController.name);

  constructor(
    private readonly subscriptionsService: SubscriptionsService,
    private readonly paddleService: PaddleService,
  ) {}

  /**
   * GET /subscriptions/plans — Get all available plans (public)
   */
  @Public()
  @Get("plans")
  @ApiOperation({ summary: "Get all available subscription plans" })
  @ApiOkResponse({ description: "List of available plans" })
  getPlans(): ApiResponse<readonly PlanInfo[]> {
    const plans = this.subscriptionsService.getPlans();
    return createSuccessResponse(plans, "Plans retrieved");
  }

  /**
   * GET /subscriptions/me — Get current user subscription
   */
  @ApiBearerAuth()
  @Get("me")
  @ApiOperation({ summary: "Get current user subscription status" })
  async getMySubscription(
    @CurrentUser() user: AuthenticatedUser,
  ): Promise<ApiResponse<SubscriptionResponseDto | null>> {
    const subscription = await this.subscriptionsService.getCurrentSubscription(
      user.id,
    );
    return createSuccessResponse(subscription, "Subscription retrieved");
  }

  /**
   * POST /subscriptions/checkout — Get checkout config for Paddle.js
   */
  @ApiBearerAuth()
  @Post("checkout")
  @ApiOperation({
    summary: "Get Paddle checkout configuration for a plan",
  })
  async getCheckoutConfig(
    @CurrentUser() user: AuthenticatedUser,
    @Body() dto: CreateCheckoutDto,
  ): Promise<
    ApiResponse<{
      priceId: string;
      clientToken: string;
      environment: string;
      userId: string;
    }>
  > {
    if (dto.plan === PlanType.FREE) {
      throw new ForbiddenException("Cannot checkout for free plan");
    }

    const config = this.subscriptionsService.getCheckoutConfig(
      dto.plan,
      dto.billingInterval,
    );

    return createSuccessResponse(
      {
        ...config,
        userId: user.id,
      },
      "Checkout config ready",
    );
  }

  /**
   * POST /subscriptions/cancel — Cancel current subscription
   */
  @ApiBearerAuth()
  @Post("cancel")
  @ApiOperation({ summary: "Cancel the current subscription" })
  async cancelSubscription(
    @CurrentUser() user: AuthenticatedUser,
    @Body() _dto: CancelSubscriptionDto,
  ): Promise<ApiResponse<null>> {
    await this.subscriptionsService.cancelSubscription(user.id);
    return createSuccessResponse(null, "Subscription cancellation requested");
  }

  /**
   * POST /subscriptions/webhook/paddle — Paddle webhook receiver
   *
   * This endpoint is PUBLIC (no JWT required) — Paddle calls it directly.
   * Authentication is done via HMAC signature verification.
   */
  @Public()
  @Post("webhook/paddle")
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: "Paddle webhook receiver (internal)" })
  async handlePaddleWebhook(
    @Req() req: RawBodyRequest<Request>,
    @Res() res: Response,
  ): Promise<void> {
    const signature = req.headers["paddle-signature"] as string | undefined;

    if (!signature) {
      this.logger.warn("Paddle webhook received without signature");
      res.status(HttpStatus.BAD_REQUEST).json({ error: "Missing signature" });
      return;
    }

    // Get raw body for signature verification
    const rawBody = req.rawBody?.toString("utf8");
    if (!rawBody) {
      this.logger.warn("Paddle webhook received without raw body");
      res.status(HttpStatus.BAD_REQUEST).json({ error: "Missing body" });
      return;
    }

    // Verify signature
    const isValid = this.paddleService.verifyWebhookSignature(
      rawBody,
      signature,
    );

    if (!isValid) {
      this.logger.warn("Paddle webhook signature verification failed");
      res.status(HttpStatus.UNAUTHORIZED).json({ error: "Invalid signature" });
      return;
    }

    // Parse and process
    try {
      const event = JSON.parse(rawBody) as PaddleWebhookEvent;
      await this.subscriptionsService.handleWebhookEvent(event);
      res.status(HttpStatus.OK).json({ received: true });
    } catch (error: unknown) {
      const err = error as Error;
      this.logger.error(`Webhook processing failed: ${err.message}`);
      res
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .json({ error: "Processing failed" });
    }
  }
}
