import { Injectable, Inject, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { FreebitClientService } from "./freebit-client.service.js";
import { FreebitRateLimiterService } from "./freebit-rate-limiter.service.js";
import { FreebitAccountService } from "./freebit-account.service.js";
import type {
  FreebitPlanChangeRequest,
  FreebitPlanChangeResponse,
} from "../interfaces/freebit.types.js";

/**
 * Service for Freebit SIM plan change operations.
 * Handles PA05-21 (plan change) endpoint.
 */
@Injectable()
export class FreebitPlanService {
  constructor(
    private readonly client: FreebitClientService,
    private readonly rateLimiter: FreebitRateLimiterService,
    private readonly accountService: FreebitAccountService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Change SIM plan
   * Uses PA05-21 changePlan endpoint
   *
   * IMPORTANT CONSTRAINTS:
   * - Requires runTime parameter set to 1st of following month (YYYYMMDDHHmm format)
   * - Does NOT take effect immediately (unlike PA05-06 and PA05-38)
   * - Must be done AFTER PA05-06 and PA05-38 (with 30-minute gaps)
   * - Cannot coexist with PA02-04 (cancellation) - plan changes will cancel the cancellation
   * - Must run 30 minutes apart from PA05-06 and PA05-38
   */
  async changeSimPlan(
    account: string,
    newPlanCode: string,
    options: { assignGlobalIp?: boolean; scheduledAt?: string } = {}
  ): Promise<{ ipv4?: string | undefined; ipv6?: string | undefined }> {
    try {
      return await this.rateLimiter.executeWithSpacing(account, "plan", async () => {
        // First, get current SIM details to log for debugging
        let currentPlanCode: string | undefined;
        try {
          const simDetails = await this.accountService.getSimDetails(account);
          currentPlanCode = simDetails.planCode;
          this.logger.log(`Current SIM plan details before change`, {
            account,
            currentPlanCode: simDetails.planCode,
            status: simDetails.status,
            simType: simDetails.simType,
          });
        } catch (detailsError) {
          this.logger.warn(`Could not fetch current SIM details`, {
            account,
            error: extractErrorMessage(detailsError),
          });
        }

        // PA05-21 requires runTime parameter in YYYYMMDD format (8 digits, date only)
        // If not provided, default to 1st of next month
        let runTime = options.scheduledAt || undefined;
        if (!runTime) {
          const nextMonth = new Date();
          nextMonth.setMonth(nextMonth.getMonth() + 1);
          nextMonth.setDate(1);
          const year = nextMonth.getFullYear();
          const month = String(nextMonth.getMonth() + 1).padStart(2, "0");
          const day = "01";
          runTime = `${year}${month}${day}`;
          this.logger.log(`No scheduledAt provided, defaulting to 1st of next month: ${runTime}`, {
            account,
            runTime,
          });
        }

        const request: Omit<FreebitPlanChangeRequest, "authKey"> = {
          account,
          planCode: newPlanCode,
          runTime: runTime,
          ...(options.assignGlobalIp === true ? { globalip: "1" } : {}),
        };

        this.logger.log(`Attempting to change SIM plan via PA05-21`, {
          account,
          currentPlanCode,
          newPlanCode,
          planCode: newPlanCode,
          globalip: request.globalip,
          runTime: request.runTime,
          scheduledAt: options.scheduledAt,
        });

        const response = await this.client.makeAuthenticatedRequest<
          FreebitPlanChangeResponse,
          typeof request
        >("/mvno/changePlan/", request);

        this.logger.log(`Successfully changed plan for account ${account} to ${newPlanCode}`, {
          account,
          newPlanCode,
          assignGlobalIp: options.assignGlobalIp,
          scheduled: !!options.scheduledAt,
          response: {
            resultCode: response.resultCode,
            statusCode: response.status?.statusCode,
            message: response.status?.message,
          },
        });

        return {
          ipv4: response.ipv4,
          ipv6: response.ipv6,
        };
      });
    } catch (error) {
      const message = extractErrorMessage(error);

      const errorDetails: Record<string, unknown> = {
        account,
        newPlanCode,
        planCode: newPlanCode,
        globalip: options.assignGlobalIp ? "1" : undefined,
        runTime: options.scheduledAt,
        error: message,
      };

      if (error instanceof Error) {
        errorDetails["errorName"] = error.name;
        errorDetails["errorMessage"] = error.message;

        if ("resultCode" in error) {
          errorDetails["resultCode"] = (error as Record<string, unknown>)["resultCode"];
        }
        if ("statusCode" in error) {
          errorDetails["statusCode"] = (error as Record<string, unknown>)["statusCode"];
        }
        if ("statusMessage" in error) {
          errorDetails["statusMessage"] = (error as Record<string, unknown>)["statusMessage"];
        }
      }

      this.logger.error(`Failed to change SIM plan for account ${account}`, errorDetails);
      throw new BadRequestException(`Failed to change SIM plan: ${message}`);
    }
  }
}
