import { Injectable, Inject } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { BaseCatalogService } from "./base-catalog.service";
import { CatalogCacheService } from "./catalog-cache.service";
import type {
  SalesforceProduct2WithPricebookEntries,
  InternetPlanCatalogItem,
  InternetInstallationCatalogItem,
  InternetAddonCatalogItem,
} from "@customer-portal/domain/catalog";
import {
  Providers as CatalogProviders,
  enrichInternetPlanMetadata,
  inferAddonTypeFromSku,
  inferInstallationTermFromSku,
} from "@customer-portal/domain/catalog";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { SalesforceConnection } from "@bff/integrations/salesforce/services/salesforce-connection.service";
import { Logger } from "nestjs-pino";
import { getErrorMessage } from "@bff/core/utils/error.util";
import { assertSalesforceId } from "@bff/integrations/salesforce/utils/soql.util";
import { buildAccountEligibilityQuery } from "@bff/integrations/salesforce/utils/catalog-query-builder";

interface SalesforceAccount {
  Id: string;
  Internet_Eligibility__c?: string;
}

@Injectable()
export class InternetCatalogService extends BaseCatalogService {
  constructor(
    sf: SalesforceConnection,
    configService: ConfigService,
    @Inject(Logger) logger: Logger,
    private mappingsService: MappingsService,
    private catalogCache: CatalogCacheService
  ) {
    super(sf, configService, logger);
  }

  async getPlans(): Promise<InternetPlanCatalogItem[]> {
    const cacheKey = this.catalogCache.buildCatalogKey("internet", "plans");

    return this.catalogCache.getCachedCatalog(
      cacheKey,
      async () => {
        const soql = this.buildCatalogServiceQuery("Internet", [
          "Internet_Plan_Tier__c",
          "Internet_Offering_Type__c",
          "Catalog_Order__c",
        ]);
        const records = await this.executeQuery<SalesforceProduct2WithPricebookEntries>(
          soql,
          "Internet Plans"
        );

        return records.map(record => {
          const entry = this.extractPricebookEntry(record);
          const plan = CatalogProviders.Salesforce.mapInternetPlan(record, entry);
          return enrichInternetPlanMetadata(plan);
        });
      },
      {
        resolveDependencies: plans => ({
          productIds: plans.map(plan => plan.id).filter((id): id is string => Boolean(id)),
        }),
      }
    );
  }

  async getInstallations(): Promise<InternetInstallationCatalogItem[]> {
    const cacheKey = this.catalogCache.buildCatalogKey("internet", "installations");

    return this.catalogCache.getCachedCatalog(
      cacheKey,
      async () => {
        const soql = this.buildProductQuery("Internet", "Installation", [
          "Billing_Cycle__c",
          "Catalog_Order__c",
        ]);
        const records = await this.executeQuery<SalesforceProduct2WithPricebookEntries>(
          soql,
          "Internet Installations"
        );

        this.logger.log(`Found ${records.length} installation records`);

        return records
          .map(record => {
            const entry = this.extractPricebookEntry(record);
            const installation = CatalogProviders.Salesforce.mapInternetInstallation(record, entry);
            return {
              ...installation,
              catalogMetadata: {
                ...installation.catalogMetadata,
                installationTerm: inferInstallationTermFromSku(installation.sku ?? ""),
              },
            };
          })
          .sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0));
      },
      {
        resolveDependencies: installations => ({
          productIds: installations.map(item => item.id).filter((id): id is string => Boolean(id)),
        }),
      }
    );
  }

  async getAddons(): Promise<InternetAddonCatalogItem[]> {
    const cacheKey = this.catalogCache.buildCatalogKey("internet", "addons");

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

        this.logger.log(`Found ${records.length} addon records`);

        return records
          .map(record => {
            const entry = this.extractPricebookEntry(record);
            const addon = CatalogProviders.Salesforce.mapInternetAddon(record, entry);
            return {
              ...addon,
              catalogMetadata: {
                ...addon.catalogMetadata,
                addonType: inferAddonTypeFromSku(addon.sku ?? ""),
              },
            };
          })
          .sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0));
      },
      {
        resolveDependencies: addons => ({
          productIds: addons.map(addon => addon.id).filter((id): id is string => Boolean(id)),
        }),
      }
    );
  }

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

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

      // Get user's Salesforce account mapping
      const mapping = await this.mappingsService.findByUserId(userId);
      if (!mapping?.sfAccountId) {
        this.logger.warn(`No Salesforce mapping found for user ${userId}, returning all plans`);
        return allPlans;
      }

      // Get customer's eligibility from Salesforce
      const sfAccountId = assertSalesforceId(mapping.sfAccountId, "sfAccountId");
      const eligibilityKey = this.catalogCache.buildEligibilityKey("internet", sfAccountId);
      const account = await this.catalogCache.getCachedEligibility<SalesforceAccount | null>(
        eligibilityKey,
        async () => {
          const soql = buildAccountEligibilityQuery(sfAccountId);
          const accounts = await this.executeQuery(soql, "Customer Eligibility");
          return accounts.length > 0 ? (accounts[0] as unknown as SalesforceAccount) : null;
        }
      );

      if (!account) {
        this.logger.warn(`No Salesforce account found for user ${userId}, returning all plans`);
        return allPlans;
      }

      const eligibility = account.Internet_Eligibility__c;

      if (!eligibility) {
        this.logger.log(`No eligibility field for user ${userId}, filtering to Home 1G plans only`);
        const homeGPlans = allPlans.filter(plan => plan.internetOfferingType === "Home 1G");
        return homeGPlans;
      }

      // Filter plans based on eligibility
      const eligiblePlans = allPlans.filter(plan => {
        const isEligible = this.checkPlanEligibility(plan, eligibility);
        if (!isEligible) {
          this.logger.debug(
            `Plan ${plan.name} (${plan.internetPlanTier ?? "Unknown"}) not eligible for user ${userId} with eligibility: ${eligibility}`
          );
        }
        return isEligible;
      });

      this.logger.log(
        `Filtered ${allPlans.length} plans to ${eligiblePlans.length} eligible plans for user ${userId} with eligibility: ${eligibility}`
      );
      return eligiblePlans;
    } catch (error) {
      this.logger.error(`Failed to get eligible plans for user ${userId}`, {
        error: getErrorMessage(error),
      });
      // Fallback to all plans if there's an error
      return this.getPlans();
    }
  }

  private checkPlanEligibility(plan: InternetPlanCatalogItem, eligibility: string): boolean {
    // Simple match: user's eligibility field must equal plan's Salesforce offering type
    // e.g., eligibility "Home 1G" matches plan.internetOfferingType "Home 1G"
    return plan.internetOfferingType === eligibility;
  }
}
