import {
  Controller,
  Get,
  Post,
  Param,
  Query,
  Body,
  Request,
  Header,
  UseGuards,
} from "@nestjs/common";
import { SimOrchestrator } from "./services/sim-orchestrator.service.js";
import { SimTopUpPricingService } from "./services/queries/sim-topup-pricing.service.js";
import { SimPlanService } from "./services/mutations/sim-plan.service.js";
import { SimCancellationService } from "./services/mutations/sim-cancellation.service.js";
import { EsimManagementService } from "./services/mutations/esim-management.service.js";
import { AdminGuard } from "@bff/core/security/guards/admin.guard.js";
import { createZodDto, ZodResponse } from "nestjs-zod";
import type { RequestWithUser } from "@bff/modules/auth/auth.types.js";

import {
  subscriptionIdParamSchema,
  simActionResponseSchema,
  simPlanChangeResultSchema,
} from "@customer-portal/domain/subscriptions";
import type { SimActionResponse, SimPlanChangeResult } from "@customer-portal/domain/subscriptions";
import {
  simTopUpRequestSchema,
  simPlanChangeRequestSchema,
  simCancelRequestSchema,
  simFeaturesUpdateRequestSchema,
  simTopUpHistoryRequestSchema,
  simChangePlanFullRequestSchema,
  simCancelFullRequestSchema,
  simReissueFullRequestSchema,
  simTopUpPricingSchema,
  simTopUpPricingPreviewRequestSchema,
  simTopUpPricingPreviewResponseSchema,
  simReissueEsimRequestSchema,
  simInfoSchema,
  simDetailsSchema,
  simUsageSchema,
  simTopUpHistorySchema,
  simAvailablePlanArraySchema,
  simCancellationPreviewSchema,
  type SimAvailablePlan,
  type SimCancellationPreview,
} from "@customer-portal/domain/sim";

// DTOs
class SubscriptionIdParamDto extends createZodDto(subscriptionIdParamSchema) {}
class SimTopUpRequestDto extends createZodDto(simTopUpRequestSchema) {}
class SimPlanChangeRequestDto extends createZodDto(simPlanChangeRequestSchema) {}
class SimCancelRequestDto extends createZodDto(simCancelRequestSchema) {}
class SimFeaturesUpdateRequestDto extends createZodDto(simFeaturesUpdateRequestSchema) {}
class SimChangePlanFullRequestDto extends createZodDto(simChangePlanFullRequestSchema) {}
class SimCancelFullRequestDto extends createZodDto(simCancelFullRequestSchema) {}
class SimReissueFullRequestDto extends createZodDto(simReissueFullRequestSchema) {}
class SimTopUpPricingPreviewRequestDto extends createZodDto(simTopUpPricingPreviewRequestSchema) {}
class SimReissueEsimRequestDto extends createZodDto(simReissueEsimRequestSchema) {}
class SimTopUpHistoryRequestDto extends createZodDto(simTopUpHistoryRequestSchema) {}

// Response DTOs
class SimInfoDto extends createZodDto(simInfoSchema) {}
class SimDetailsDto extends createZodDto(simDetailsSchema) {}
class SimUsageDto extends createZodDto(simUsageSchema) {}
class SimTopUpHistoryDto extends createZodDto(simTopUpHistorySchema) {}
class SimActionResponseDto extends createZodDto(simActionResponseSchema) {}
class SimPlanChangeResultDto extends createZodDto(simPlanChangeResultSchema) {}
class SimTopUpPricingResponseDto extends createZodDto(simTopUpPricingSchema) {}
class SimTopUpPricingPreviewResponseDto extends createZodDto(
  simTopUpPricingPreviewResponseSchema
) {}
class SimAvailablePlansResponseDto extends createZodDto(simAvailablePlanArraySchema) {}
class SimCancellationPreviewResponseDto extends createZodDto(simCancellationPreviewSchema) {}

@Controller("subscriptions")
export class SimController {
  constructor(
    private readonly simOrchestrator: SimOrchestrator,
    private readonly simTopUpPricingService: SimTopUpPricingService,
    private readonly simPlanService: SimPlanService,
    private readonly simCancellationService: SimCancellationService,
    private readonly esimManagementService: EsimManagementService
  ) {}

  // ==================== Static SIM Routes (must be before :id routes) ====================

  @Get("sim/top-up/pricing")
  @Header("Cache-Control", "public, max-age=3600")
  @ZodResponse({ description: "Get SIM top-up pricing", type: SimTopUpPricingResponseDto })
  async getSimTopUpPricing() {
    const pricing = await this.simTopUpPricingService.getTopUpPricing();
    return pricing;
  }

  @Get("sim/top-up/pricing/preview")
  @Header("Cache-Control", "public, max-age=3600")
  @ZodResponse({
    description: "Preview SIM top-up pricing",
    type: SimTopUpPricingPreviewResponseDto,
  })
  async previewSimTopUpPricing(@Query() query: SimTopUpPricingPreviewRequestDto) {
    const preview = await this.simTopUpPricingService.calculatePricingPreview(query.quotaMb);
    return preview;
  }

  @Get("debug/sim-details/:account")
  @UseGuards(AdminGuard)
  async debugSimDetails(@Param("account") account: string) {
    return await this.simOrchestrator.getSimDetailsDirectly(account);
  }

  // ==================== Subscription-specific SIM Routes ====================

  @Get(":id/sim/debug")
  @UseGuards(AdminGuard)
  async debugSimSubscription(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto
  ): Promise<Record<string, unknown>> {
    return this.simOrchestrator.debugSimSubscription(req.user.id, params.id);
  }

  @Get(":id/sim")
  @ZodResponse({ description: "Get SIM info", type: SimInfoDto })
  async getSimInfo(@Request() req: RequestWithUser, @Param() params: SubscriptionIdParamDto) {
    return this.simOrchestrator.getSimInfo(req.user.id, params.id);
  }

  @Get(":id/sim/details")
  @ZodResponse({ description: "Get SIM details", type: SimDetailsDto })
  async getSimDetails(@Request() req: RequestWithUser, @Param() params: SubscriptionIdParamDto) {
    return this.simOrchestrator.getSimDetails(req.user.id, params.id);
  }

  @Get(":id/sim/usage")
  @ZodResponse({ description: "Get SIM usage", type: SimUsageDto })
  async getSimUsage(@Request() req: RequestWithUser, @Param() params: SubscriptionIdParamDto) {
    return this.simOrchestrator.getSimUsage(req.user.id, params.id);
  }

  @Get(":id/sim/top-up-history")
  @ZodResponse({ description: "Get SIM top-up history", type: SimTopUpHistoryDto })
  async getSimTopUpHistory(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto,
    @Query() query: SimTopUpHistoryRequestDto
  ) {
    return this.simOrchestrator.getSimTopUpHistory(req.user.id, params.id, query);
  }

  @Post(":id/sim/top-up")
  @ZodResponse({ description: "Top up SIM", type: SimActionResponseDto })
  async topUpSim(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto,
    @Body() body: SimTopUpRequestDto
  ): Promise<SimActionResponse> {
    await this.simOrchestrator.topUpSim(req.user.id, params.id, body);
    return { message: "SIM top-up completed successfully" };
  }

  @Post(":id/sim/change-plan")
  @ZodResponse({ description: "Change SIM plan", type: SimPlanChangeResultDto })
  async changeSimPlan(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto,
    @Body() body: SimPlanChangeRequestDto
  ): Promise<SimPlanChangeResult> {
    const result = await this.simOrchestrator.changeSimPlan(req.user.id, params.id, body);
    return {
      message: "SIM plan change completed successfully",
      ...result,
    };
  }

  @Post(":id/sim/cancel")
  @ZodResponse({ description: "Cancel SIM", type: SimActionResponseDto })
  async cancelSim(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto,
    @Body() body: SimCancelRequestDto
  ): Promise<SimActionResponse> {
    await this.simOrchestrator.cancelSim(req.user.id, params.id, body);
    return { message: "SIM cancellation completed successfully" };
  }

  @Post(":id/sim/reissue-esim")
  @ZodResponse({ description: "Reissue eSIM profile", type: SimActionResponseDto })
  async reissueEsimProfile(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto,
    @Body() body: SimReissueEsimRequestDto
  ): Promise<SimActionResponse> {
    await this.simOrchestrator.reissueEsimProfile(
      req.user.id,
      params.id,
      body.newEid ? { newEid: body.newEid } : {}
    );
    return { message: "eSIM profile reissue completed successfully" };
  }

  @Post(":id/sim/features")
  @ZodResponse({ description: "Update SIM features", type: SimActionResponseDto })
  async updateSimFeatures(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto,
    @Body() body: SimFeaturesUpdateRequestDto
  ): Promise<SimActionResponse> {
    await this.simOrchestrator.updateSimFeatures(req.user.id, params.id, body);
    return { message: "SIM features updated successfully" };
  }

  // ==================== Enhanced SIM Management Endpoints ====================

  @Get(":id/sim/available-plans")
  @Header("Cache-Control", "private, max-age=300")
  @ZodResponse({ description: "Get available SIM plans", type: SimAvailablePlansResponseDto })
  async getAvailablePlans(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto
  ): Promise<SimAvailablePlan[]> {
    const plans = await this.simPlanService.getAvailablePlans(req.user.id, params.id);
    return plans;
  }

  @Post(":id/sim/change-plan-full")
  @ZodResponse({ description: "Change SIM plan (full)", type: SimPlanChangeResultDto })
  async changeSimPlanFull(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto,
    @Body() body: SimChangePlanFullRequestDto
  ): Promise<SimPlanChangeResult> {
    const result = await this.simPlanService.changeSimPlanFull(req.user.id, params.id, body);
    return {
      message: `SIM plan change scheduled for ${result.scheduledAt}`,
      ...result,
    };
  }

  @Get(":id/sim/cancellation-preview")
  @Header("Cache-Control", "private, max-age=60")
  @ZodResponse({
    description: "Get SIM cancellation preview",
    type: SimCancellationPreviewResponseDto,
  })
  async getCancellationPreview(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto
  ): Promise<SimCancellationPreview> {
    const preview = await this.simCancellationService.getCancellationPreview(
      req.user.id,
      params.id
    );
    return preview;
  }

  @Post(":id/sim/cancel-full")
  @ZodResponse({ description: "Cancel SIM (full)", type: SimActionResponseDto })
  async cancelSimFull(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto,
    @Body() body: SimCancelFullRequestDto
  ): Promise<SimActionResponse> {
    await this.simCancellationService.cancelSimFull(req.user.id, params.id, body);
    return {
      message: `SIM cancellation scheduled for end of ${body.cancellationMonth}`,
    };
  }

  @Post(":id/sim/reissue")
  @ZodResponse({ description: "Reissue SIM", type: SimActionResponseDto })
  async reissueSim(
    @Request() req: RequestWithUser,
    @Param() params: SubscriptionIdParamDto,
    @Body() body: SimReissueFullRequestDto
  ): Promise<SimActionResponse> {
    await this.esimManagementService.reissueSim(req.user.id, params.id, body);

    if (body.simType === "esim") {
      return { message: "eSIM profile reissue request submitted" };
    } else {
      return {
        message: "Physical SIM reissue request submitted. You will be contacted shortly.",
      };
    }
  }
}
