import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { Logger } from "nestjs-pino";
import { Injectable, NotFoundException, Inject } from "@nestjs/common";
import { WhmcsOperationException } from "@bff/core/exceptions/domain-exceptions.js";
import {
  filterSubscriptionsByStatus,
  transformWhmcsSubscriptionListResponse,
} from "@customer-portal/domain/subscriptions/providers";
import type { Subscription, SubscriptionList } from "@customer-portal/domain/subscriptions";
import { WhmcsConnectionFacade } from "../facades/whmcs.facade.js";
import { WhmcsCurrencyService } from "./whmcs-currency.service.js";
import { WhmcsCacheService } from "../cache/whmcs-cache.service.js";
import type { WhmcsGetClientsProductsParams } from "@customer-portal/domain/subscriptions/providers";

export interface SubscriptionFilters {
  status?: string;
}

@Injectable()
export class WhmcsSubscriptionService {
  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly connectionService: WhmcsConnectionFacade,
    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 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,
        });
      }

      let result: SubscriptionList;
      try {
        result = transformWhmcsSubscriptionListResponse(rawResponse, {
          ...this.currencyService.getTransformContext(),
          onItemError: (error, product) => {
            this.logger.error(`Failed to transform subscription ${product.id}`, {
              error: extractErrorMessage(error),
            });
          },
        });
      } catch (error) {
        const message = error instanceof Error ? error.message : "GetClientsProducts call failed";
        this.logger.error("WHMCS GetClientsProducts returned error result", {
          clientId,
          error: extractErrorMessage(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 filterSubscriptionsByStatus(result, filters.status);
      }

      return result;
    } catch (error) {
      this.logger.error(`Failed to fetch subscriptions for client ${clientId}`, {
        error: extractErrorMessage(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;
      }

      // 2. Check if we have the FULL list cached.
      // If we do, searching memory is faster than an API call.
      const cachedList = await this.cacheService.getSubscriptionsList(userId);
      if (cachedList) {
        const found = cachedList.subscriptions.find((s: Subscription) => s.id === subscriptionId);
        if (found) {
          this.logger.debug(
            `Cache hit (via list) for subscription: user ${userId}, subscription ${subscriptionId}`
          );
          // Cache this individual item for faster direct access next time
          await this.cacheService.setSubscription(userId, subscriptionId, found);
          return found;
        }
        // If list is cached but item not found, it might be new or not in that list?
        // Proceed to fetch single item.
      }

      // 3. Fetch ONLY this subscription from WHMCS (Optimized)
      // Instead of fetching all products, use serviceid filter
      const params: WhmcsGetClientsProductsParams = {
        clientid: clientId,
        serviceid: subscriptionId,
      };

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

      // Transform response
      const resultList = transformWhmcsSubscriptionListResponse(
        rawResponse,
        this.currencyService.getTransformContext()
      );

      const subscription = resultList.subscriptions.find(s => 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: extractErrorMessage(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}`
    );
  }
}
