import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { SimDetailsService } from "./sim-details.service";
import { SimUsageService } from "./sim-usage.service";
import { SimTopUpService } from "./sim-topup.service";
import { SimPlanService } from "./sim-plan.service";
import { SimCancellationService } from "./sim-cancellation.service";
import { EsimManagementService } from "./esim-management.service";
import { SimValidationService } from "./sim-validation.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import type {
  SimDetails,
  SimUsage,
  SimTopUpHistory,
} from "@bff/integrations/freebit/interfaces/freebit.types";
import type {
  SimTopUpRequest,
  SimPlanChangeRequest,
  SimCancelRequest,
  SimTopUpHistoryRequest,
  SimFeaturesUpdateRequest,
} from "../types/sim-requests.types";

@Injectable()
export class SimOrchestratorService {
  constructor(
    private readonly simDetails: SimDetailsService,
    private readonly simUsage: SimUsageService,
    private readonly simTopUp: SimTopUpService,
    private readonly simPlan: SimPlanService,
    private readonly simCancellation: SimCancellationService,
    private readonly esimManagement: EsimManagementService,
    private readonly simValidation: SimValidationService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Get SIM details for a subscription
   */
  async getSimDetails(userId: string, subscriptionId: number): Promise<SimDetails> {
    return this.simDetails.getSimDetails(userId, subscriptionId);
  }

  /**
   * Get SIM data usage for a subscription
   */
  async getSimUsage(userId: string, subscriptionId: number): Promise<SimUsage> {
    return this.simUsage.getSimUsage(userId, subscriptionId);
  }

  /**
   * Top up SIM data quota with payment processing
   */
  async topUpSim(userId: string, subscriptionId: number, request: SimTopUpRequest): Promise<void> {
    return this.simTopUp.topUpSim(userId, subscriptionId, request);
  }

  /**
   * Get SIM top-up history
   */
  async getSimTopUpHistory(
    userId: string,
    subscriptionId: number,
    request: SimTopUpHistoryRequest
  ): Promise<SimTopUpHistory> {
    return this.simUsage.getSimTopUpHistory(userId, subscriptionId, request);
  }

  /**
   * Change SIM plan
   */
  async changeSimPlan(
    userId: string,
    subscriptionId: number,
    request: SimPlanChangeRequest
  ): Promise<{ ipv4?: string; ipv6?: string }> {
    return this.simPlan.changeSimPlan(userId, subscriptionId, request);
  }

  /**
   * Update SIM features (voicemail, call waiting, roaming, network type)
   */
  async updateSimFeatures(
    userId: string,
    subscriptionId: number,
    request: SimFeaturesUpdateRequest
  ): Promise<void> {
    return this.simPlan.updateSimFeatures(userId, subscriptionId, request);
  }

  /**
   * Cancel SIM service
   */
  async cancelSim(
    userId: string,
    subscriptionId: number,
    request: SimCancelRequest = {}
  ): Promise<void> {
    return this.simCancellation.cancelSim(userId, subscriptionId, request);
  }

  /**
   * Reissue eSIM profile
   */
  async reissueEsimProfile(userId: string, subscriptionId: number, newEid?: string): Promise<void> {
    return this.esimManagement.reissueEsimProfile(userId, subscriptionId, newEid);
  }

  /**
   * Get comprehensive SIM information (details + usage combined)
   */
  async getSimInfo(
    userId: string,
    subscriptionId: number
  ): Promise<{
    details: SimDetails;
    usage: SimUsage;
  }> {
    try {
      const [details, usage] = await Promise.all([
        this.getSimDetails(userId, subscriptionId),
        this.getSimUsage(userId, subscriptionId),
      ]);

      // If Freebit doesn't return remaining quota, derive it from plan code (e.g., PASI_50G)
      // by subtracting measured usage (today + recentDays) from the plan cap.
      const normalizeNumber = (n: number) => (isFinite(n) && n > 0 ? n : 0);
      const usedMb =
        normalizeNumber(usage.todayUsageMb) +
        (usage.recentDaysUsage || []).reduce((sum, d) => sum + normalizeNumber(d.usageMb), 0);

      const planCapMatch = (details.planCode || "").match(/(\d+)\s*G/i);
      if ((details.remainingQuotaMb === 0 || details.remainingQuotaMb == null) && planCapMatch) {
        const capGb = parseInt(planCapMatch[1], 10);
        if (!isNaN(capGb) && capGb > 0) {
          const capMb = capGb * 1000;
          const remainingMb = Math.max(capMb - usedMb, 0);
          details.remainingQuotaMb = Math.round(remainingMb * 100) / 100;
          details.remainingQuotaKb = Math.round(details.remainingQuotaMb * 1000);
        }
      }

      return { details, usage };
    } catch (error) {
      const sanitizedError = getErrorMessage(error);
      this.logger.error(`Failed to get comprehensive SIM info for subscription ${subscriptionId}`, {
        error: sanitizedError,
        userId,
        subscriptionId,
      });
      throw error;
    }
  }

  /**
   * Debug method to check subscription data for SIM services
   */
  async debugSimSubscription(
    userId: string,
    subscriptionId: number
  ): Promise<Record<string, unknown>> {
    return this.simValidation.debugSimSubscription(userId, subscriptionId);
  }

  /**
   * Debug method to query Freebit directly for any account's details
   * Bypasses subscription validation and queries Freebit directly
   */
  async getSimDetailsDirectly(account: string): Promise<SimDetails> {
    return this.simDetails.getSimDetailsDirectly(account);
  }
}
