import { Injectable, Inject } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { BaseServicesService } from "./base-services.service.js";
import { ServicesCacheService } from "./services-cache.service.js";
import type {
  SimCatalogProduct,
  SimActivationFeeCatalogItem,
} from "@customer-portal/domain/services";
import type { SalesforceProduct2WithPricebookEntries } from "@customer-portal/domain/services/providers";
import { mapSimActivationFee, mapSimProduct } from "@customer-portal/domain/services/providers";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service.js";
import { SalesforceConnection } from "@bff/integrations/salesforce/services/salesforce-connection.service.js";
import { Logger } from "nestjs-pino";
import { WhmcsConnectionFacade } from "@bff/integrations/whmcs/facades/whmcs.facade.js";

@Injectable()
export class SimServicesService extends BaseServicesService {
  // eslint-disable-next-line max-params -- NestJS dependency injection requires all services to be injected via constructor
  constructor(
    sf: SalesforceConnection,
    configService: ConfigService,
    @Inject(Logger) logger: Logger,
    private mappingsService: MappingsService,
    private whmcs: WhmcsConnectionFacade,
    private catalogCache: ServicesCacheService
  ) {
    super(sf, configService, logger);
  }

  async getPlans(): Promise<SimCatalogProduct[]> {
    const cacheKey = this.catalogCache.buildServicesKey("sim", "plans");

    return this.catalogCache.getCachedServices(
      cacheKey,
      async () => {
        const soql = this.buildServicesQuery("Sim", [
          "SIM_Data_Size__c",
          "SIM_Plan_Type__c",
          "SIM_Has_Family_Discount__c",
          "Catalog_Order__c",
        ]);
        const records = await this.executeQuery<SalesforceProduct2WithPricebookEntries>(
          soql,
          "SIM Plans"
        );

        return records.map(record => {
          const entry = this.extractPricebookEntry(record);
          const product = mapSimProduct(record, entry);

          return {
            ...product,
            description: product.description ?? product.name,
          } satisfies SimCatalogProduct;
        });
      },
      {
        resolveDependencies: plans => ({
          productIds: plans.map(plan => plan.id).filter((id): id is string => Boolean(id)),
        }),
      }
    );
  }

  async getActivationFees(): Promise<SimActivationFeeCatalogItem[]> {
    const cacheKey = this.catalogCache.buildServicesKey("sim", "activation-fees");

    return this.catalogCache.getCachedServices(
      cacheKey,
      async () => {
        const soql = this.buildProductQuery("Sim", "Activation", ["Catalog_Order__c"]);
        const records = await this.executeQuery<SalesforceProduct2WithPricebookEntries>(
          soql,
          "SIM Activation Fees"
        );

        const activationFees = records
          .map(record => {
            const entry = this.extractPricebookEntry(record);
            return mapSimActivationFee(record, entry);
          })
          .sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0));

        if (activationFees.length === 0) {
          this.logger.warn("No SIM activation fees found in catalog");
          return activationFees;
        }

        const hasDefault = activationFees.some(fee => fee.catalogMetadata?.isDefault === true);

        if (!hasDefault) {
          this.logger.warn(
            "No default SIM activation fee configured. Marking the first fee as default."
          );
          const firstFee = activationFees[0];
          if (firstFee) {
            activationFees[0] = {
              ...firstFee,
              catalogMetadata: {
                ...firstFee.catalogMetadata,
                isDefault: true,
              },
            };
          }
        }

        return activationFees;
      },
      {
        resolveDependencies: products => ({
          productIds: products.map(product => product.id).filter((id): id is string => Boolean(id)),
        }),
      }
    );
  }

  async getAddons(): Promise<SimCatalogProduct[]> {
    const cacheKey = this.catalogCache.buildServicesKey("sim", "addons");

    return this.catalogCache.getCachedServices(
      cacheKey,
      async () => {
        const soql = this.buildProductQuery("Sim", "Add-on", [
          "Billing_Cycle__c",
          "Catalog_Order__c",
          "Bundled_Addon__c",
          "Is_Bundled_Addon__c",
        ]);
        const records = await this.executeQuery<SalesforceProduct2WithPricebookEntries>(
          soql,
          "SIM Add-ons"
        );

        return records
          .map(record => {
            const entry = this.extractPricebookEntry(record);
            const product = mapSimProduct(record, entry);

            return {
              ...product,
              description: product.description ?? product.name,
            } satisfies SimCatalogProduct;
          })
          .sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0));
      },
      {
        resolveDependencies: products => ({
          productIds: products.map(product => product.id).filter((id): id is string => Boolean(id)),
        }),
      }
    );
  }

  async getPlansForUser(userId: string): Promise<SimCatalogProduct[]> {
    try {
      // Get all plans first
      const allPlans = await this.getPlans();

      // Check if user has existing SIM services
      const hasExistingSim = await this.checkUserHasExistingSim(userId);

      if (hasExistingSim) {
        this.logger.log(`User ${userId} has existing SIM, showing family discount plans`);
        // Show family discount plans + regular plans for comparison
        return allPlans; // Family plans are included with simHasFamilyDiscount: true
      } else {
        this.logger.log(`User ${userId} has no existing SIM, showing regular plans only`);
        // Show only regular plans (hide family discount plans)
        return allPlans.filter(plan => !plan.simHasFamilyDiscount);
      }
    } catch (error) {
      this.logger.error(`Failed to get personalized SIM plans for user ${userId}`, error);
      // Fallback to all regular plans
      const allPlans = await this.getPlans();
      return allPlans.filter(plan => !plan.simHasFamilyDiscount);
    }
  }

  private async checkUserHasExistingSim(userId: string): Promise<boolean> {
    try {
      // Get user's WHMCS client ID
      const mapping = await this.mappingsService.findByUserId(userId);
      if (!mapping?.whmcsClientId) {
        return false;
      }

      const cacheKey = this.catalogCache.buildServicesKey(
        "sim",
        "has-existing-sim",
        String(mapping.whmcsClientId)
      );

      // This is per-account and can be somewhat expensive (WHMCS call).
      // Cache briefly to reduce repeat reads during account page refreshes.
      return await this.catalogCache.getCachedVolatile(cacheKey, async () => {
        const products = await this.whmcs.getClientsProducts({ clientid: mapping.whmcsClientId });
        const services = (products?.products?.product || []) as Array<{
          groupname?: string;
          status?: string;
        }>;

        // Look for active SIM services
        return services.some(service => {
          const group = String(service.groupname || "").toLowerCase();
          const status = String(service.status || "").toLowerCase();
          return group.includes("sim") && status === "active";
        });
      });
    } catch (error) {
      this.logger.warn(`Failed to check existing SIM for user ${userId}`, error);
      return false; // Default to no existing SIM
    }
  }

  async getCatalogData() {
    const [plans, activationFees, addons] = await Promise.all([
      this.getPlans(),
      this.getActivationFees(),
      this.getAddons(),
    ]);
    return { plans, activationFees, addons };
  }
}
