import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import type {
  FreebitAccountDetailsResponse,
  FreebitTrafficInfoResponse,
  FreebitQuotaHistoryResponse,
  SimDetails,
  SimUsage,
  SimTopUpHistory,
} from "../interfaces/freebit.types";
import type { SimVoiceOptionsService } from "@bff/modules/subscriptions/sim-management/services/sim-voice-options.service";

@Injectable()
export class FreebitMapperService {
  constructor(
    @Inject(Logger) private readonly logger: Logger,
    @Inject("SimVoiceOptionsService") private readonly voiceOptionsService?: SimVoiceOptionsService
  ) {}

  private parseOptionFlag(value: unknown, defaultValue: boolean = false): boolean {
    // If value is undefined or null, return the default
    if (value === undefined || value === null) {
      return defaultValue;
    }
    
    if (typeof value === "boolean") {
      return value;
    }
    if (typeof value === "number") {
      return value === 10 || value === 1;
    }
    if (typeof value === "string") {
      const normalized = value.trim().toLowerCase();
      if (normalized === "on" || normalized === "true") {
        return true;
      }
      if (normalized === "off" || normalized === "false") {
        return false;
      }
      const numeric = Number(normalized);
      if (!Number.isNaN(numeric)) {
        return numeric === 10 || numeric === 1;
      }
    }
    return defaultValue;
  }

  /**
   * Map SIM status from Freebit API to domain status
   */
  mapSimStatus(status: string): "active" | "suspended" | "cancelled" | "pending" {
    switch (status) {
      case "active":
        return "active";
      case "suspended":
        return "suspended";
      case "temporary":
      case "waiting":
        return "pending";
      case "obsolete":
        return "cancelled";
      default:
        return "pending";
    }
  }

  /**
   * Map Freebit account details response to SimDetails
   */
  async mapToSimDetails(response: FreebitAccountDetailsResponse): Promise<SimDetails> {
    const account = response.responseDatas[0];
    if (!account) {
      throw new Error("No account data in response");
    }

    let simType: "standard" | "nano" | "micro" | "esim" = "standard";
    if (account.eid) {
      simType = "esim";
    } else if (account.simSize) {
      simType = account.simSize;
    }

    // Try to get voice options from database first
    let voiceMailEnabled = true;
    let callWaitingEnabled = true;
    let internationalRoamingEnabled = true;
    let networkType = String(account.networkType ?? account.contractLine ?? "4G");

    if (this.voiceOptionsService) {
      try {
        const storedOptions = await this.voiceOptionsService.getVoiceOptions(
          String(account.account ?? "")
        );

        if (storedOptions) {
          voiceMailEnabled = storedOptions.voiceMailEnabled;
          callWaitingEnabled = storedOptions.callWaitingEnabled;
          internationalRoamingEnabled = storedOptions.internationalRoamingEnabled;
          networkType = storedOptions.networkType;

          this.logger.debug("[FreebitMapper] Loaded voice options from database", {
            account: account.account,
            options: storedOptions,
          });
        } else {
          // No stored options, check API response
          voiceMailEnabled = this.parseOptionFlag(account.voicemail ?? account.voiceMail, true);
          callWaitingEnabled = this.parseOptionFlag(
            account.callwaiting ?? account.callWaiting,
            true
          );
          internationalRoamingEnabled = this.parseOptionFlag(
            account.worldwing ?? account.worldWing,
            true
          );

          this.logger.debug(
            "[FreebitMapper] No stored options found, using defaults or API values",
            {
              account: account.account,
              voiceMailEnabled,
              callWaitingEnabled,
              internationalRoamingEnabled,
            }
          );
        }
      } catch (error) {
        this.logger.warn("[FreebitMapper] Failed to load voice options from database", {
          account: account.account,
          error,
        });
      }
    }

    return {
      account: String(account.account ?? ""),
      status: this.mapSimStatus(String(account.state ?? account.status ?? "pending")),
      planCode: String(account.planCode ?? ""),
      planName: String(account.planName ?? ""),
      simType,
      iccid: String(account.iccid ?? ""),
      eid: String(account.eid ?? ""),
      msisdn: String(account.msisdn ?? account.account ?? ""),
      imsi: String(account.imsi ?? ""),
      remainingQuotaMb: Number(account.remainingQuotaMb ?? account.quota ?? 0),
      remainingQuotaKb: Number(account.remainingQuotaKb ?? 0),
      voiceMailEnabled,
      callWaitingEnabled,
      internationalRoamingEnabled,
      networkType,
      activatedAt: account.startDate ? String(account.startDate) : undefined,
      expiresAt: account.async ? String(account.async.date) : undefined,
      ipv4: account.ipv4,
      ipv6: account.ipv6,
      startDate: account.startDate ? String(account.startDate) : undefined,
      hasVoice: account.talk === 10,
      hasSms: account.sms === 10,
    };
  }

  /**
   * Map Freebit traffic info response to SimUsage
   */
  mapToSimUsage(response: FreebitTrafficInfoResponse): SimUsage {
    if (!response.traffic) {
      throw new Error("No traffic data in response");
    }

    const todayUsageKb = parseInt(response.traffic.today, 10) || 0;
    const recentDaysData = response.traffic.inRecentDays.split(",").map((usage, index) => ({
      date: new Date(Date.now() - (index + 1) * 24 * 60 * 60 * 1000).toISOString().split("T")[0],
      usageKb: parseInt(usage, 10) || 0,
      usageMb: Math.round(((parseInt(usage, 10) || 0) / 1024) * 100) / 100,
    }));

    return {
      account: String(response.account ?? ""),
      todayUsageMb: Math.round((todayUsageKb / 1024) * 100) / 100,
      todayUsageKb,
      recentDaysUsage: recentDaysData,
      isBlacklisted: response.traffic.blackList === "10",
    };
  }

  /**
   * Map Freebit quota history response to SimTopUpHistory
   */
  mapToSimTopUpHistory(response: FreebitQuotaHistoryResponse, account: string): SimTopUpHistory {
    if (!response.quotaHistory) {
      throw new Error("No history data in response");
    }

    return {
      account,
      totalAdditions: Number(response.total) || 0,
      additionCount: Number(response.count) || 0,
      history: response.quotaHistory.map(item => ({
        quotaKb: parseInt(item.quota, 10),
        quotaMb: Math.round((parseInt(item.quota, 10) / 1024) * 100) / 100,
        addedDate: item.date,
        expiryDate: item.expire,
        campaignCode: item.quotaCode,
      })),
    };
  }

  /**
   * Normalize account identifier (remove formatting)
   */
  normalizeAccount(account: string): string {
    return account.replace(/[-\s()]/g, "");
  }

  /**
   * Validate account format
   */
  validateAccount(account: string): boolean {
    const normalized = this.normalizeAccount(account);
    // Basic validation - should be digits, typically 10-11 digits for Japanese phone numbers
    return /^\d{10,11}$/.test(normalized);
  }

  /**
   * Format date for Freebit API (YYYYMMDD)
   */
  formatDateForApi(date: Date): string {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}${month}${day}`;
  }

  /**
   * Parse date from Freebit API format (YYYYMMDD)
   */
  parseDateFromApi(dateString: string): Date | null {
    if (!/^\d{8}$/.test(dateString)) {
      return null;
    }

    const year = parseInt(dateString.substring(0, 4), 10);
    const month = parseInt(dateString.substring(4, 6), 10) - 1; // Month is 0-indexed
    const day = parseInt(dateString.substring(6, 8), 10);

    return new Date(year, month, day);
  }
}
