import {
  Controller,
  Get,
  Post,
  Param,
  Query,
  Request,
  ParseIntPipe,
  HttpCode,
  HttpStatus,
  BadRequestException,
} from "@nestjs/common";
import { InvoicesOrchestratorService } from "./services/invoices-orchestrator.service";
import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { ZodValidationPipe } from "@customer-portal/validation/nestjs";
import type { RequestWithUser } from "@bff/modules/auth/auth.types";

import type {
  Invoice,
  InvoiceList,
  InvoiceSsoLink,
  InvoiceListQuery,
} from "@customer-portal/domain/billing";
import { invoiceListQuerySchema, invoiceSchema } from "@customer-portal/domain/billing";
import type { Subscription } from "@customer-portal/domain/subscriptions";
import type {
  PaymentMethodList,
  PaymentGatewayList,
  InvoicePaymentLink,
} from "@customer-portal/domain/payments";

/**
 * Invoice Controller
 *
 * All request validation is handled by Zod schemas via ZodValidationPipe.
 * Business logic is delegated to service layer.
 */
@Controller("invoices")
export class InvoicesController {
  constructor(
    private readonly invoicesService: InvoicesOrchestratorService,
    private readonly whmcsService: WhmcsService,
    private readonly mappingsService: MappingsService
  ) {}

  @Get()
  async getInvoices(
    @Request() req: RequestWithUser,
    @Query(new ZodValidationPipe(invoiceListQuerySchema)) query: InvoiceListQuery
  ): Promise<InvoiceList> {
    return this.invoicesService.getInvoices(req.user.id, query);
  }

  @Get("payment-methods")
  async getPaymentMethods(@Request() req: RequestWithUser): Promise<PaymentMethodList> {
    const mapping = await this.mappingsService.findByUserId(req.user.id);
    if (!mapping?.whmcsClientId) {
      throw new Error("WHMCS client mapping not found");
    }
    return this.whmcsService.getPaymentMethods(mapping.whmcsClientId, req.user.id);
  }

  @Get("payment-gateways")
  async getPaymentGateways(): Promise<PaymentGatewayList> {
    return this.whmcsService.getPaymentGateways();
  }

  @Post("payment-methods/refresh")
  @HttpCode(HttpStatus.OK)
  async refreshPaymentMethods(@Request() req: RequestWithUser): Promise<PaymentMethodList> {
    // Invalidate cache first
    await this.whmcsService.invalidatePaymentMethodsCache(req.user.id);

    // Return fresh payment methods
    const mapping = await this.mappingsService.findByUserId(req.user.id);
    if (!mapping?.whmcsClientId) {
      throw new Error("WHMCS client mapping not found");
    }
    return this.whmcsService.getPaymentMethods(mapping.whmcsClientId, req.user.id);
  }

  @Get(":id")
  async getInvoiceById(
    @Request() req: RequestWithUser,
    @Param("id", ParseIntPipe) invoiceId: number
  ): Promise<Invoice> {
    // Validate using domain schema
    invoiceSchema.shape.id.parse(invoiceId);
    return this.invoicesService.getInvoiceById(req.user.id, invoiceId);
  }

  @Get(":id/subscriptions")
  getInvoiceSubscriptions(
    @Request() _req: RequestWithUser,
    @Param("id", ParseIntPipe) invoiceId: number
  ): Subscription[] {
    // Validate using domain schema
    invoiceSchema.shape.id.parse(invoiceId);

    // This functionality has been moved to WHMCS directly
    // For now, return empty array as subscriptions are managed in WHMCS
    return [];
  }

  @Post(":id/sso-link")
  @HttpCode(HttpStatus.OK)
  async createSsoLink(
    @Request() req: RequestWithUser,
    @Param("id", ParseIntPipe) invoiceId: number,
    @Query("target") target?: "view" | "download" | "pay"
  ): Promise<InvoiceSsoLink> {
    // Validate using domain schema
    invoiceSchema.shape.id.parse(invoiceId);

    // Validate target parameter
    if (target && !["view", "download", "pay"].includes(target)) {
      throw new BadRequestException('Target must be "view", "download", or "pay"');
    }

    const mapping = await this.mappingsService.findByUserId(req.user.id);
    if (!mapping?.whmcsClientId) {
      throw new Error("WHMCS client mapping not found");
    }

    const ssoUrl = await this.whmcsService.whmcsSsoForInvoice(
      mapping.whmcsClientId,
      invoiceId,
      target || "view"
    );

    return {
      url: ssoUrl,
      expiresAt: new Date(Date.now() + 60000).toISOString(), // 60 seconds per WHMCS spec
    };
  }

  @Post(":id/payment-link")
  @HttpCode(HttpStatus.OK)
  async createPaymentLink(
    @Request() req: RequestWithUser,
    @Param("id", ParseIntPipe) invoiceId: number,
    @Query("paymentMethodId") paymentMethodId?: string,
    @Query("gatewayName") gatewayName?: string
  ): Promise<InvoicePaymentLink> {
    // Validate using domain schema
    invoiceSchema.shape.id.parse(invoiceId);

    const paymentMethodIdNum = paymentMethodId ? parseInt(paymentMethodId, 10) : undefined;
    if (paymentMethodId && (isNaN(paymentMethodIdNum!) || paymentMethodIdNum! <= 0)) {
      throw new BadRequestException("Payment method ID must be a positive number");
    }

    const mapping = await this.mappingsService.findByUserId(req.user.id);
    if (!mapping?.whmcsClientId) {
      throw new Error("WHMCS client mapping not found");
    }

    const ssoResult = await this.whmcsService.createPaymentSsoToken(
      mapping.whmcsClientId,
      invoiceId,
      paymentMethodIdNum,
      gatewayName || "stripe"
    );

    return {
      url: ssoResult.url,
      expiresAt: ssoResult.expiresAt,
      gatewayName: gatewayName || "stripe",
    };
  }
}
