import { Injectable, Inject, OnModuleInit, OnModuleDestroy } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { getErrorMessage } from "@bff/core/utils/error.util";
import { WhmcsOperationException } from "@bff/core/exceptions/domain-exceptions";
import { WhmcsConnectionOrchestratorService } from "../connection/services/whmcs-connection-orchestrator.service";
import {
  FALLBACK_CURRENCY,
  type WhmcsCurrenciesResponse,
  type WhmcsCurrency,
} from "@customer-portal/domain/billing";

@Injectable()
export class WhmcsCurrencyService implements OnModuleInit, OnModuleDestroy {
  private defaultCurrency: WhmcsCurrency | null = null;
  private currencies: WhmcsCurrency[] = [];
  private readonly REFRESH_INTERVAL_MS = 6 * 60 * 60 * 1000; // 6 hours
  private refreshTimer: NodeJS.Timeout | null = null;

  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly connectionService: WhmcsConnectionOrchestratorService
  ) {}

  async onModuleInit() {
    try {
      // Check if WHMCS is available before trying to load currencies
      this.logger.debug("Checking WHMCS availability before loading currencies");
      const isAvailable = await this.connectionService.isAvailable();

      if (!isAvailable) {
        this.logger.warn("WHMCS service is not available, using fallback currency configuration");
        this.setFallbackCurrency();
        return;
      }

      this.logger.debug("WHMCS is available, attempting to load currencies");
      await this.loadCurrencies();
    } catch (error) {
      this.logger.error("Failed to load WHMCS currencies on startup", {
        error: getErrorMessage(error),
      });
      // Set fallback default
      this.setFallbackCurrency();
    } finally {
      this.startRefreshLoop();
    }
  }

  onModuleDestroy(): void {
    if (this.refreshTimer) {
      clearInterval(this.refreshTimer);
      this.refreshTimer = null;
    }
  }

  /**
   * Set fallback currency configuration when WHMCS is not available
   */
  private setFallbackCurrency(): void {
    const fallback = { ...FALLBACK_CURRENCY };
    this.defaultCurrency = fallback;
    this.currencies = [fallback];

    this.logger.log("Using fallback currency configuration", {
      defaultCurrency: this.defaultCurrency.code,
    });
  }

  /**
   * Get the default currency (first currency from WHMCS or JPY fallback)
   */
  getDefaultCurrency(): WhmcsCurrency {
    return this.defaultCurrency ? { ...this.defaultCurrency } : { ...FALLBACK_CURRENCY };
  }

  /**
   * Get all available currencies
   */
  getAllCurrencies(): WhmcsCurrency[] {
    return this.currencies;
  }

  /**
   * Find currency by code
   */
  getCurrencyByCode(code: string): WhmcsCurrency | null {
    return this.currencies.find(c => c.code.toUpperCase() === code.toUpperCase()) || null;
  }

  /**
   * Load currencies from WHMCS
   */
  private async loadCurrencies(): Promise<void> {
    try {
      // The connection service returns the raw WHMCS API response data
      // (the WhmcsResponse wrapper is unwrapped by the API methods service)
      const response = (await this.connectionService.getCurrencies()) as WhmcsCurrenciesResponse;

      // Check if response has currencies data (success case) or error fields
      if (response.result === "success" || (response.currencies && !response.error)) {
        // Parse the WHMCS response format into currency objects
        this.currencies = this.parseWhmcsCurrenciesResponse(response);

        if (this.currencies.length > 0) {
          // Set first currency as default (WHMCS typically returns the primary currency first)
          this.defaultCurrency = this.currencies[0];

          this.logger.log(`Loaded ${this.currencies.length} currencies from WHMCS`, {
            defaultCurrency: this.defaultCurrency?.code,
            allCurrencies: this.currencies.map(c => c.code),
          });
        } else {
          throw new WhmcsOperationException("No currencies found in WHMCS response", {
            operation: "getCurrencies",
          });
        }
      } else {
        this.logger.error("WHMCS GetCurrencies returned error", {
          result: response?.result,
          message: response?.message,
          error: response?.error,
          errorcode: response?.errorcode,
          fullResponse: JSON.stringify(response, null, 2),
        });
        throw new WhmcsOperationException(
          `WHMCS GetCurrencies error: ${response?.message || response?.error || "Unknown error"}`,
          { operation: "getCurrencies" }
        );
      }
    } catch (error) {
      this.logger.error("Failed to load currencies from WHMCS", {
        error: getErrorMessage(error),
      });
      throw error;
    }
  }

  /**
   * Parse WHMCS response format into currency objects
   * Handles both flat format (currencies[currency][0][id]) and nested format (currencies.currency[])
   */
  private parseWhmcsCurrenciesResponse(response: WhmcsCurrenciesResponse): WhmcsCurrency[] {
    const currencies: WhmcsCurrency[] = [];

    // Check if response has nested currency structure
    if (
      response.currencies &&
      typeof response.currencies === "object" &&
      "currency" in response.currencies
    ) {
      const currencyArray = Array.isArray(response.currencies.currency)
        ? response.currencies.currency
        : [response.currencies.currency];

      for (const currencyData of currencyArray) {
        const currency: WhmcsCurrency = {
          id: parseInt(String(currencyData.id)) || 0,
          code: String(currencyData.code || ""),
          prefix: String(currencyData.prefix || ""),
          suffix: String(currencyData.suffix || ""),
          format: String(currencyData.format || "1"),
          rate: String(currencyData.rate || "1.00000"),
        };

        // Validate that we have essential currency data
        if (currency.id && currency.code) {
          currencies.push(currency);
        }
      }
    } else {
      // Fallback: try to parse flat format (currencies[currency][0][id], etc.)
      const currencyKeys = Object.keys(response).filter(
        key => key.startsWith("currencies[currency][") && key.includes("][id]")
      );

      // Extract currency indices
      const currencyIndices = currencyKeys
        .map(key => {
          const match = key.match(/currencies\[currency\]\[(\d+)\]\[id\]/);
          return match ? parseInt(match[1], 10) : null;
        })
        .filter((index): index is number => index !== null);

      // Build currency objects from the flat response
      for (const index of currencyIndices) {
        const currency: WhmcsCurrency = {
          id: parseInt(String(response[`currencies[currency][${index}][id]`])) || 0,
          code: String(response[`currencies[currency][${index}][code]`] || ""),
          prefix: String(response[`currencies[currency][${index}][prefix]`] || ""),
          suffix: String(response[`currencies[currency][${index}][suffix]`] || ""),
          format: String(response[`currencies[currency][${index}][format]`] || "1"),
          rate: String(response[`currencies[currency][${index}][rate]`] || "1.00000"),
        };

        // Validate that we have essential currency data
        if (currency.id && currency.code) {
          currencies.push(currency);
        }
      }
    }

    return currencies;
  }

  /**
   * Refresh currencies from WHMCS (can be called manually if needed)
   */
  async refreshCurrencies(): Promise<void> {
    try {
      await this.loadCurrencies();
    } catch (error) {
      this.logger.error("Currency refresh failed", {
        error: getErrorMessage(error),
      });
      if (!this.defaultCurrency || this.currencies.length === 0) {
        this.setFallbackCurrency();
      }
    }
  }

  private startRefreshLoop(): void {
    if (this.refreshTimer) {
      return;
    }

    this.refreshTimer = setInterval(() => {
      void this.refreshCurrencies();
    }, this.REFRESH_INTERVAL_MS);

    if (typeof this.refreshTimer.unref === "function") {
      this.refreshTimer.unref();
    }
  }
}
