import { Injectable, Inject, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { FreebitOrchestratorService } from "@bff/integrations/freebit/services/freebit-orchestrator.service";
import { SimValidationService } from "./sim-validation.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import type { SimPlanChangeRequest, SimFeaturesUpdateRequest, SimChangePlanFullRequest } from "@customer-portal/domain/sim";
import { SimScheduleService } from "./sim-schedule.service";
import { SimActionRunnerService } from "./sim-action-runner.service";
import { SimManagementQueueService } from "../queue/sim-management.queue";
import { SimApiNotificationService } from "./sim-api-notification.service";
import { SimCatalogService } from "@bff/modules/catalog/services/sim-catalog.service";
import type { SimCatalogProduct } from "@customer-portal/domain/catalog";

// Mapping from Salesforce SKU to Freebit plan code
const SKU_TO_FREEBIT_PLAN_CODE: Record<string, string> = {
  "SIM-DATA-VOICE-5GB": "PASI_5G",
  "SIM-DATA-VOICE-10GB": "PASI_10G",
  "SIM-DATA-VOICE-25GB": "PASI_25G",
  "SIM-DATA-VOICE-50GB": "PASI_50G",
  "SIM-DATA-ONLY-5GB": "PASI_5G_DATA",
  "SIM-DATA-ONLY-10GB": "PASI_10G_DATA",
  "SIM-DATA-ONLY-25GB": "PASI_25G_DATA",
  "SIM-DATA-ONLY-50GB": "PASI_50G_DATA",
  "SIM-VOICE-ONLY": "PASI_VOICE",
};

// Reverse mapping: Freebit plan code to Salesforce SKU
const FREEBIT_PLAN_CODE_TO_SKU: Record<string, string> = Object.fromEntries(
  Object.entries(SKU_TO_FREEBIT_PLAN_CODE).map(([sku, code]) => [code, sku])
);

export interface AvailablePlan extends SimCatalogProduct {
  freebitPlanCode: string;
  isCurrentPlan: boolean;
}

@Injectable()
export class SimPlanService {
  constructor(
    private readonly freebitService: FreebitOrchestratorService,
    private readonly simValidation: SimValidationService,
    private readonly simSchedule: SimScheduleService,
    private readonly simActionRunner: SimActionRunnerService,
    private readonly simQueue: SimManagementQueueService,
    private readonly apiNotification: SimApiNotificationService,
    private readonly simCatalog: SimCatalogService,
    private readonly configService: ConfigService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  private get freebitBaseUrl(): string {
    return this.configService.get<string>("FREEBIT_BASE_URL") || "https://i1.mvno.net/emptool/api";
  }

  /**
   * Get available plans for plan change
   * Filters by current plan type (e.g., only show DataSmsVoice plans if current is DataSmsVoice)
   */
  async getAvailablePlans(
    userId: string,
    subscriptionId: number
  ): Promise<AvailablePlan[]> {
    const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
    const simDetails = await this.freebitService.getSimDetails(validation.account);
    const currentPlanCode = simDetails.planCode;
    const currentSku = FREEBIT_PLAN_CODE_TO_SKU[currentPlanCode];

    // Get all plans from Salesforce
    const allPlans = await this.simCatalog.getPlans();

    // Determine current plan type
    let currentPlanType: string | undefined;
    if (currentSku) {
      const currentPlan = allPlans.find(p => p.sku === currentSku);
      currentPlanType = currentPlan?.simPlanType;
    }

    // Filter plans by type (e.g., only show DataSmsVoice if current is DataSmsVoice)
    const filteredPlans = currentPlanType
      ? allPlans.filter(p => p.simPlanType === currentPlanType)
      : allPlans.filter(p => !p.simHasFamilyDiscount); // Default: non-family plans

    // Map to AvailablePlan with Freebit codes
    return filteredPlans.map(plan => {
      const freebitPlanCode = SKU_TO_FREEBIT_PLAN_CODE[plan.sku] || plan.sku;
      return {
        ...plan,
        freebitPlanCode,
        isCurrentPlan: freebitPlanCode === currentPlanCode,
      };
    });
  }

  /**
   * Get Freebit plan code from Salesforce SKU
   */
  getFreebitPlanCode(sku: string): string | undefined {
    return SKU_TO_FREEBIT_PLAN_CODE[sku];
  }

  /**
   * Get Salesforce SKU from Freebit plan code
   */
  getSalesforceSku(planCode: string): string | undefined {
    return FREEBIT_PLAN_CODE_TO_SKU[planCode];
  }

  /**
   * Change SIM plan (basic)
   */
  async changeSimPlan(
    userId: string,
    subscriptionId: number,
    request: SimPlanChangeRequest
  ): Promise<{ ipv4?: string; ipv6?: string }> {
    let account = "";
    const assignGlobalIp = request.assignGlobalIp ?? false;

    const response = await this.simActionRunner.run(
      "Change Plan",
      {
        baseContext: {
          userId,
          subscriptionId,
          newPlanCode: request.newPlanCode,
          assignGlobalIp,
        },
        enrichSuccess: result => ({
          account: result.account,
          scheduledAt: result.scheduledAt,
        }),
        enrichError: () => ({
          account,
        }),
      },
      async () => {
        const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
        account = validation.account;

        if (!request.newPlanCode || request.newPlanCode.length < 3) {
          throw new BadRequestException("Invalid plan code");
        }

        const scheduleResolution = this.simSchedule.resolveScheduledDate(request.scheduledAt);

        this.logger.log("Submitting SIM plan change request", {
          userId,
          subscriptionId,
          account,
          newPlanCode: request.newPlanCode,
          scheduledAt: scheduleResolution.date,
          assignGlobalIp,
          scheduleOrigin: scheduleResolution.source,
        });

        const result = await this.freebitService.changeSimPlan(account, request.newPlanCode, {
          assignGlobalIp,
          scheduledAt: scheduleResolution.date,
        });

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

        return {
          ...result,
          account,
          scheduledAt: scheduleResolution.date,
        };
      }
    );

    return {
      ipv4: response.ipv4,
      ipv6: response.ipv6,
    };
  }

  /**
   * Change SIM plan with enhanced notifications and Salesforce SKU mapping
   */
  async changeSimPlanFull(
    userId: string,
    subscriptionId: number,
    request: SimChangePlanFullRequest
  ): Promise<{ ipv4?: string; ipv6?: string; scheduledAt?: string }> {
    const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
    const account = validation.account;

    // Get or derive Freebit plan code from SKU
    const freebitPlanCode = SKU_TO_FREEBIT_PLAN_CODE[request.newPlanSku] || request.newPlanCode;

    if (!freebitPlanCode || freebitPlanCode.length < 3) {
      throw new BadRequestException("Invalid plan code");
    }

    // Always schedule for 1st of following month
    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 scheduledAt = `${year}${month}01`;

    this.logger.log("Submitting SIM plan change request (full)", {
      userId,
      subscriptionId,
      account,
      newPlanSku: request.newPlanSku,
      freebitPlanCode,
      scheduledAt,
    });

    const result = await this.freebitService.changeSimPlan(account, freebitPlanCode, {
      assignGlobalIp: request.assignGlobalIp ?? false,
      scheduledAt,
    });

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

    // Send API results email
    await this.apiNotification.sendApiResultsEmail(
      "API results - Plan Change",
      [
        {
          url: `${this.freebitBaseUrl}/mvno/changePlan/`,
          json: {
            account,
            planCode: freebitPlanCode,
            runTime: scheduledAt,
            authKey: "[REDACTED]",
          },
          result: {
            resultCode: "100",
            status: { message: "OK", statusCode: "200" },
            ipv4: result.ipv4 || "",
            ipv6: result.ipv6 || "",
          },
        },
      ],
      `Plan changed to: ${request.newPlanName || freebitPlanCode}\nScheduled for: ${scheduledAt}`
    );

    return {
      ipv4: result.ipv4,
      ipv6: result.ipv6,
      scheduledAt,
    };
  }

  /**
   * Update SIM features (voicemail, call waiting, roaming, network type)
   */
  async updateSimFeatures(
    userId: string,
    subscriptionId: number,
    request: SimFeaturesUpdateRequest
  ): Promise<void> {
    let account = "";

    await this.simActionRunner.run(
      "Update Features",
      {
        baseContext: {
          userId,
          subscriptionId,
          ...request,
        },
        enrichSuccess: () => ({
          account,
        }),
        enrichError: () => ({
          account,
        }),
      },
      async () => {
        const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
        account = validation.account;

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

        const doVoice =
          typeof request.voiceMailEnabled === "boolean" ||
          typeof request.callWaitingEnabled === "boolean" ||
          typeof request.internationalRoamingEnabled === "boolean";
        const doContract = typeof request.networkType === "string";

        if (doVoice && doContract) {
          await this.freebitService.updateSimFeatures(account, {
            voiceMailEnabled: request.voiceMailEnabled,
            callWaitingEnabled: request.callWaitingEnabled,
            internationalRoamingEnabled: request.internationalRoamingEnabled,
          });

          await this.simQueue.scheduleNetworkTypeChange({
            account,
            networkType: request.networkType as "4G" | "5G",
            userId,
            subscriptionId,
          });

          this.logger.log("Scheduled contract line change via queue after voice option change", {
            userId,
            subscriptionId,
            account,
            networkType: request.networkType,
          });
        } else {
          await this.freebitService.updateSimFeatures(account, request);
        }

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