import { Injectable, Inject, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { FreebitOrchestratorService } from "@bff/integrations/freebit/services/freebit-orchestrator.service";
import { SimValidationService } from "./sim-validation.service";
import { SimNotificationService } from "./sim-notification.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import type { SimPlanChangeRequest, SimFeaturesUpdateRequest } from "../types/sim-requests.types";

@Injectable()
export class SimPlanService {
  constructor(
    private readonly freebitService: FreebitOrchestratorService,
    private readonly simValidation: SimValidationService,
    private readonly simNotification: SimNotificationService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Change SIM plan
   */
  async changeSimPlan(
    userId: string,
    subscriptionId: number,
    request: SimPlanChangeRequest
  ): Promise<{ ipv4?: string; ipv6?: string }> {
    try {
      const { account } = await this.simValidation.validateSimSubscription(userId, subscriptionId);

      // Validate plan code format - simplified for 5GB, 10GB, 25GB, 50GB plans
      if (!request.newPlanCode || !["5GB", "10GB", "25GB", "50GB"].includes(request.newPlanCode)) {
        throw new BadRequestException("Invalid plan code. Must be one of: 5GB, 10GB, 25GB, 50GB");
      }

      // Map simplified plan codes to Freebit plan codes
      const planCodeMapping: Record<string, string> = {
        "5GB": "PASI_5G",
        "10GB": "PASI_10G",
        "25GB": "PASI_25G",
        "50GB": "PASI_50G",
      };

      const freebitPlanCode = planCodeMapping[request.newPlanCode];
      if (!freebitPlanCode) {
        throw new BadRequestException(`Unable to map plan code ${request.newPlanCode} to Freebit format`);
      }

      // Automatically set to 1st of next month
      const nextMonth = new Date();
      nextMonth.setMonth(nextMonth.getMonth() + 1);
      nextMonth.setDate(1); // Set to 1st of the month

      // Format as YYYYMMDD for Freebit API
      const year = nextMonth.getFullYear();
      const month = String(nextMonth.getMonth() + 1).padStart(2, "0");
      const day = String(nextMonth.getDate()).padStart(2, "0");
      const scheduledAt = `${year}${month}${day}`;

      this.logger.log(`Auto-scheduled plan change to 1st of next month: ${scheduledAt}`, {
        userId,
        subscriptionId,
        account,
        newPlanCode: request.newPlanCode,
        freebitPlanCode,
      });

      // First try immediate change, fallback to scheduled if that fails
      let result: { ipv4?: string; ipv6?: string };
      
      try {
        result = await this.freebitService.changeSimPlan(account, freebitPlanCode, {
          assignGlobalIp: false, // No global IP assignment
          // Try immediate first
        });

        this.logger.log(`Immediate plan change successful for account ${account}`, {
          account,
          newPlanCode: request.newPlanCode,
          freebitPlanCode,
        });
      } catch (immediateError) {
        this.logger.warn(`Immediate plan change failed, trying scheduled: ${getErrorMessage(immediateError)}`, {
          account,
          newPlanCode: request.newPlanCode,
          freebitPlanCode,
          error: getErrorMessage(immediateError),
        });

        // Fallback to scheduled change
        result = await this.freebitService.changeSimPlan(account, freebitPlanCode, {
          assignGlobalIp: false, // No global IP assignment
          scheduledAt: scheduledAt,
        });
        
        this.logger.log(`Scheduled plan change successful for account ${account}`, {
          account,
          newPlanCode: request.newPlanCode,
          freebitPlanCode,
          scheduledAt,
        });
      }

      this.logger.log(`Successfully changed SIM plan for subscription ${subscriptionId}`, {
        userId,
        subscriptionId,
        account,
        newPlanCode: request.newPlanCode,
        freebitPlanCode,
        scheduledAt: scheduledAt,
        assignGlobalIp: false,
      });

      await this.simNotification.notifySimAction("Change Plan", "SUCCESS", {
        userId,
        subscriptionId,
        account,
        newPlanCode: request.newPlanCode,
        freebitPlanCode,
        scheduledAt,
      });

      return result;
    } catch (error) {
      const sanitizedError = getErrorMessage(error);
      this.logger.error(`Failed to change SIM plan for subscription ${subscriptionId}`, {
        error: sanitizedError,
        userId,
        subscriptionId,
        newPlanCode: request.newPlanCode,
      });

      await this.simNotification.notifySimAction("Change Plan", "ERROR", {
        userId,
        subscriptionId,
        account: "unknown",
        newPlanCode: request.newPlanCode,
        error: sanitizedError,
      });

      throw error;
    }
  }

  /**
   * Update SIM features (voicemail, call waiting, roaming, network type)
   */
  async updateSimFeatures(
    userId: string,
    subscriptionId: number,
    request: SimFeaturesUpdateRequest
  ): Promise<void> {
    try {
      const { account } = await this.simValidation.validateSimSubscription(userId, subscriptionId);

      // Validate network type if provided
      if (request.networkType && !["4G", "5G"].includes(request.networkType)) {
        throw new BadRequestException('networkType must be either "4G" or "5G"');
      }

      // Log the request for debugging
      this.logger.log(`Updating SIM features for subscription ${subscriptionId}`, {
        userId,
        subscriptionId,
        account,
        request,
      });

      // Update all features in one call - the FreebitOperationsService will handle the complexity
      await this.freebitService.updateSimFeatures(account, {
        voiceMailEnabled: request.voiceMailEnabled,
        callWaitingEnabled: request.callWaitingEnabled,
        internationalRoamingEnabled: request.internationalRoamingEnabled,
        networkType: request.networkType,
      });

      this.logger.log(`Successfully updated SIM features for subscription ${subscriptionId}`, {
        userId,
        subscriptionId,
        account,
        ...request,
      });

      await this.simNotification.notifySimAction("Update Features", "SUCCESS", {
        userId,
        subscriptionId,
        account,
        ...request,
      });
    } catch (error) {
      const sanitizedError = getErrorMessage(error);
      this.logger.error(`Failed to update SIM features for subscription ${subscriptionId}`, {
        error: sanitizedError,
        userId,
        subscriptionId,
        account: "unknown",
        ...request,
        errorStack: error instanceof Error ? error.stack : undefined,
      });
      await this.simNotification.notifySimAction("Update Features", "ERROR", {
        userId,
        subscriptionId,
        ...request,
        error: sanitizedError,
      });
      throw error;
    }
  }
}
