import { getErrorMessage } from "@bff/core/utils/error.util";
import { Logger } from "nestjs-pino";
import { Injectable, NotFoundException, Inject } from "@nestjs/common";
import { WhmcsOperationException } from "@bff/core/exceptions/domain-exceptions";
import { Subscription, SubscriptionList, Providers } from "@customer-portal/domain/subscriptions";
import { WhmcsConnectionOrchestratorService } from "../connection/services/whmcs-connection-orchestrator.service";
import { WhmcsCurrencyService } from "./whmcs-currency.service";
import { WhmcsCacheService } from "../cache/whmcs-cache.service";
import { type WhmcsGetClientsProductsParams } from "@customer-portal/domain/subscriptions";

export interface SubscriptionFilters {
  status?: string;
}

@Injectable()
export class WhmcsSubscriptionService {
  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly connectionService: WhmcsConnectionOrchestratorService,
    private readonly currencyService: WhmcsCurrencyService,
    private readonly cacheService: WhmcsCacheService
  ) {}

  /**
   * Get client subscriptions/services with caching
   */
  async getSubscriptions(
    clientId: number,
    userId: string,
    filters: SubscriptionFilters = {}
  ): Promise<SubscriptionList> {
    try {
      // Try cache first
      const cached = await this.cacheService.getSubscriptionsList(userId);
      if (cached) {
        this.logger.debug(`Cache hit for subscriptions: user ${userId}`);

        // Apply status filter if needed
        if (filters.status) {
          return Providers.Whmcs.filterSubscriptionsByStatus(cached, filters.status);
        }

        return cached;
      }

      // Fetch from WHMCS API
      const params: WhmcsGetClientsProductsParams = {
        clientid: clientId,
        orderby: "regdate",
        order: "DESC",
      };

      const rawResponse = await this.connectionService.getClientsProducts(params);

      if (!rawResponse) {
        this.logger.error("WHMCS GetClientsProducts returned empty response", {
          clientId,
        });
        throw new WhmcsOperationException("GetClientsProducts call failed", {
          clientId,
        });
      }

      const defaultCurrency = this.currencyService.getDefaultCurrency();
      let result: SubscriptionList;
      try {
        result = Providers.Whmcs.transformWhmcsSubscriptionListResponse(rawResponse, {
          defaultCurrencyCode: defaultCurrency.code,
          defaultCurrencySymbol: defaultCurrency.prefix || defaultCurrency.suffix,
          onItemError: (error, product) => {
            this.logger.error(`Failed to transform subscription ${product.id}`, {
              error: getErrorMessage(error),
            });
          },
        });
      } catch (error) {
        const message = error instanceof Error ? error.message : "GetClientsProducts call failed";
        this.logger.error("WHMCS GetClientsProducts returned error result", {
          clientId,
          error: getErrorMessage(error),
        });
        throw new WhmcsOperationException(message, { clientId });
      }

      // Cache the result
      await this.cacheService.setSubscriptionsList(userId, result);

      this.logger.log(
        `Fetched ${result.subscriptions.length} subscriptions for client ${clientId}`
      );

      // Apply status filter if needed
      if (filters.status) {
        return Providers.Whmcs.filterSubscriptionsByStatus(result, filters.status);
      }

      return result;
    } catch (error) {
      this.logger.error(`Failed to fetch subscriptions for client ${clientId}`, {
        error: getErrorMessage(error),
        filters,
      });
      throw error;
    }
  }

  /**
   * Get individual subscription by ID
   */
  async getSubscriptionById(
    clientId: number,
    userId: string,
    subscriptionId: number
  ): Promise<Subscription> {
    try {
      // Try cache first
      const cached = await this.cacheService.getSubscription(userId, subscriptionId);
      if (cached) {
        this.logger.debug(
          `Cache hit for subscription: user ${userId}, subscription ${subscriptionId}`
        );
        return cached;
      }

      // Get all subscriptions and find the specific one
      const subscriptionList = await this.getSubscriptions(clientId, userId);
      const subscription = subscriptionList.subscriptions.find(
        (s: Subscription) => s.id === subscriptionId
      );

      if (!subscription) {
        throw new NotFoundException(`Subscription ${subscriptionId} not found`);
      }

      // Cache the individual subscription
      await this.cacheService.setSubscription(userId, subscriptionId, subscription);

      this.logger.log(`Fetched subscription ${subscriptionId} for client ${clientId}`);
      return subscription;
    } catch (error) {
      this.logger.error(`Failed to fetch subscription ${subscriptionId} for client ${clientId}`, {
        error: getErrorMessage(error),
      });
      throw error;
    }
  }

  /**
   * Invalidate cache for a specific subscription
   */
  async invalidateSubscriptionCache(userId: string, subscriptionId: number): Promise<void> {
    await this.cacheService.invalidateSubscription(userId, subscriptionId);
    this.logger.log(
      `Invalidated subscription cache for user ${userId}, subscription ${subscriptionId}`
    );
  }
}
