/**
 * Subscriptions Controller
 *
 * Handles subscription management endpoints:
 * - GET /subscriptions - Get subscription overview
 * - GET /subscriptions/plans - Get all available plans
 * - GET /subscriptions/can-invite - Check if can invite users
 * - POST /subscriptions/checkout - Create Stripe checkout session
 * - POST /subscriptions/portal - Create Stripe portal session
 * - POST /subscriptions/webhook - Handle Stripe webhooks
 */

import {
  Controller,
  Get,
  Post,
  Body,
  UseGuards,
  HttpCode,
  HttpStatus,
  Logger,
  Headers,
  RawBodyRequest,
  Req,
  Inject,
  ForbiddenException,
} from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiBearerAuth,
  ApiExcludeEndpoint,
} from '@nestjs/swagger';
import { Request } from 'express';
import { SubscriptionService } from '../services/subscription.service';
import {
  CreateCheckoutSessionDto,
  CreatePortalSessionDto,
  SyncSubscriptionDto,
  SubscriptionOverviewResponseDto,
  CanInviteResponseDto,
  CheckoutSessionResponseDto,
  PortalSessionResponseDto,
  AllPlansResponseDto,
} from '../dto/subscription.dto';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { RolesGuard } from '../guards/roles.guard';
import { Roles } from '../decorators/roles.decorator';
import { CurrentUser, UserPayload } from '../decorators/current-user.decorator';
import { Public } from '../decorators/public.decorator';
import {
  OrganizationRepository,
  ORGANIZATION_REPOSITORY,
} from '@domain/ports/out/organization.repository';

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

  constructor(
    private readonly subscriptionService: SubscriptionService,
    @Inject(ORGANIZATION_REPOSITORY)
    private readonly organizationRepository: OrganizationRepository
  ) {}

  /**
   * Get subscription overview for current organization
   */
  @Get()
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('admin', 'manager')
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Get subscription overview',
    description:
      'Get the subscription details including licenses for the current organization. Admin/manager only.',
  })
  @ApiResponse({
    status: 200,
    description: 'Subscription overview retrieved successfully',
    type: SubscriptionOverviewResponseDto,
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - requires admin or manager role',
  })
  async getSubscriptionOverview(
    @CurrentUser() user: UserPayload
  ): Promise<SubscriptionOverviewResponseDto> {
    this.logger.log(`[User: ${user.email}] Getting subscription overview`);
    return this.subscriptionService.getSubscriptionOverview(user.organizationId, user.role);
  }

  /**
   * Get all available plans
   */
  @Get('plans')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Get all plans',
    description: 'Get details of all available subscription plans.',
  })
  @ApiResponse({
    status: 200,
    description: 'Plans retrieved successfully',
    type: AllPlansResponseDto,
  })
  getAllPlans(): AllPlansResponseDto {
    this.logger.log('Getting all subscription plans');
    return this.subscriptionService.getAllPlans();
  }

  /**
   * Check if organization can invite more users
   */
  @Get('can-invite')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('admin', 'manager')
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Check license availability',
    description:
      'Check if the organization can invite more users based on license availability. Admin/manager only.',
  })
  @ApiResponse({
    status: 200,
    description: 'License availability check result',
    type: CanInviteResponseDto,
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - requires admin or manager role',
  })
  async canInvite(@CurrentUser() user: UserPayload): Promise<CanInviteResponseDto> {
    this.logger.log(`[User: ${user.email}] Checking license availability`);
    return this.subscriptionService.canInviteUser(user.organizationId, user.role);
  }

  /**
   * Create Stripe Checkout session for subscription upgrade
   */
  @Post('checkout')
  @HttpCode(HttpStatus.OK)
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('admin', 'manager')
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Create checkout session',
    description: 'Create a Stripe Checkout session for upgrading subscription. Admin/Manager only.',
  })
  @ApiResponse({
    status: 200,
    description: 'Checkout session created successfully',
    type: CheckoutSessionResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Bad request - invalid plan or already subscribed',
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - requires admin or manager role',
  })
  async createCheckoutSession(
    @Body() dto: CreateCheckoutSessionDto,
    @CurrentUser() user: UserPayload
  ): Promise<CheckoutSessionResponseDto> {
    this.logger.log(`[User: ${user.email}] Creating checkout session for plan: ${dto.plan}`);

    // ADMIN users bypass all payment restrictions
    if (user.role !== 'ADMIN') {
      // SIRET verification gate: organization must have a verified SIRET before purchasing
      const organization = await this.organizationRepository.findById(user.organizationId);
      if (!organization || !organization.siretVerified) {
        throw new ForbiddenException(
          'Le numero SIRET de votre organisation doit etre verifie par un administrateur avant de pouvoir effectuer un achat. Contactez votre administrateur.'
        );
      }
    }

    return this.subscriptionService.createCheckoutSession(user.organizationId, user.id, dto);
  }

  /**
   * Create Stripe Customer Portal session
   */
  @Post('portal')
  @HttpCode(HttpStatus.OK)
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('admin', 'manager')
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Create portal session',
    description:
      'Create a Stripe Customer Portal session for subscription management. Admin/Manager only.',
  })
  @ApiResponse({
    status: 200,
    description: 'Portal session created successfully',
    type: PortalSessionResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Bad request - no Stripe customer found',
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - requires admin or manager role',
  })
  async createPortalSession(
    @Body() dto: CreatePortalSessionDto,
    @CurrentUser() user: UserPayload
  ): Promise<PortalSessionResponseDto> {
    this.logger.log(`[User: ${user.email}] Creating portal session`);
    return this.subscriptionService.createPortalSession(user.organizationId, dto);
  }

  /**
   * Sync subscription from Stripe
   * Useful when webhooks are not available (e.g., local development)
   */
  @Post('sync')
  @HttpCode(HttpStatus.OK)
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('admin', 'manager')
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Sync subscription from Stripe',
    description:
      'Manually sync subscription data from Stripe. Useful when webhooks are not working (local dev). Pass sessionId after checkout to sync new subscription. Admin/Manager only.',
  })
  @ApiResponse({
    status: 200,
    description: 'Subscription synced successfully',
    type: SubscriptionOverviewResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Bad request - no Stripe subscription found',
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - requires admin or manager role',
  })
  async syncFromStripe(
    @Body() dto: SyncSubscriptionDto,
    @CurrentUser() user: UserPayload
  ): Promise<SubscriptionOverviewResponseDto> {
    this.logger.log(
      `[User: ${user.email}] Syncing subscription from Stripe${dto.sessionId ? ` (sessionId: ${dto.sessionId})` : ''}`
    );
    return this.subscriptionService.syncFromStripe(user.organizationId, dto.sessionId);
  }

  /**
   * Handle Stripe webhook events
   */
  @Post('webhook')
  @Public()
  @HttpCode(HttpStatus.OK)
  @ApiExcludeEndpoint()
  async handleWebhook(
    @Headers('stripe-signature') signature: string,
    @Req() req: RawBodyRequest<Request>
  ): Promise<{ received: boolean }> {
    const rawBody = req.rawBody;
    if (!rawBody) {
      this.logger.error('No raw body found in request');
      return { received: false };
    }

    try {
      await this.subscriptionService.handleStripeWebhook(rawBody, signature);
      return { received: true };
    } catch (error) {
      this.logger.error('Webhook processing failed', error);
      return { received: false };
    }
  }
}
