/**
 * Subscription Validation Coordinator
 *
 * Centralized service for common subscription validation patterns:
 * - Account mapping validation (user → whmcsClientId + sfAccountId)
 * - Product type detection (Internet vs SIM)
 * - OpportunityId resolution (from custom fields or Salesforce lookup)
 *
 * This coordinator extracts duplicated patterns from:
 * - InternetCancellationService
 * - SimCancellationService
 * - CancellationService
 * - SimValidationService
 *
 * Benefits:
 * - Single source of truth for validation logic
 * - Consistent error messages across services
 * - Easier to maintain and test
 */

import { Injectable, Inject, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service.js";
import { SalesforceOpportunityService } from "@bff/integrations/salesforce/services/salesforce-opportunity.service.js";
import type {
  AccountMappingResult,
  OpportunityResolution,
  OpportunityResolutionOptions,
  ServiceType,
} from "./subscription-validation.types.js";

@Injectable()
export class SubscriptionValidationCoordinator {
  constructor(
    private readonly mappingsService: MappingsService,
    private readonly opportunityService: SalesforceOpportunityService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  // ============================================================================
  // Account Mapping Validation
  // ============================================================================

  /**
   * Validate that a user has complete account mapping (WHMCS + Salesforce).
   *
   * @throws BadRequestException if mapping is missing or incomplete
   */
  async validateAccountMapping(userId: string): Promise<AccountMappingResult> {
    const mapping = await this.mappingsService.findByUserId(userId);

    if (!mapping?.whmcsClientId || !mapping?.sfAccountId) {
      this.logger.warn("Account mapping not found or incomplete", {
        userId,
        hasWhmcsClientId: Boolean(mapping?.whmcsClientId),
        hasSfAccountId: Boolean(mapping?.sfAccountId),
      });
      throw new BadRequestException("Account mapping not found");
    }

    return {
      whmcsClientId: mapping.whmcsClientId,
      sfAccountId: mapping.sfAccountId,
    };
  }

  /**
   * Get WHMCS client ID only (for operations that don't need Salesforce).
   *
   * @throws BadRequestException if WHMCS mapping not found
   */
  async getWhmcsClientId(userId: string): Promise<number> {
    return this.mappingsService.getWhmcsClientIdOrThrow(userId);
  }

  // ============================================================================
  // Product Type Detection
  // ============================================================================

  /**
   * Detect service type from product name.
   *
   * Heuristics:
   * - SIM: Product name contains "sim"
   * - Internet: Contains "internet", "sonixnet", or "ntt" + "fiber"
   *
   * @throws BadRequestException if service type cannot be determined
   */
  detectServiceType(productName: string): ServiceType {
    const lower = productName.toLowerCase();

    // SIM heuristics
    if (lower.includes("sim")) {
      return "sim";
    }

    // Internet heuristics
    const isInternet =
      lower.includes("internet") ||
      lower.includes("sonixnet") ||
      (lower.includes("ntt") && lower.includes("fiber"));

    if (isInternet) {
      return "internet";
    }

    throw new BadRequestException("This endpoint is only for SIM or Internet subscriptions");
  }

  /**
   * Check if product name indicates an Internet service.
   */
  isInternetService(productName: string): boolean {
    const lower = productName.toLowerCase();
    return (
      lower.includes("internet") ||
      lower.includes("sonixnet") ||
      (lower.includes("ntt") && lower.includes("fiber"))
    );
  }

  /**
   * Check if product name indicates a SIM service.
   */
  isSimService(productName: string): boolean {
    return productName.toLowerCase().includes("sim");
  }

  // ============================================================================
  // OpportunityId Resolution
  // ============================================================================

  /**
   * Resolve OpportunityId for a subscription.
   *
   * Strategy:
   * 1. First, check WHMCS custom fields (if provided)
   * 2. If not found and fallback enabled, query Salesforce by WHMCS Service ID
   *
   * This never throws - returns null if not found (Opportunity lookup is non-critical).
   */
  async resolveOpportunityId(
    subscriptionId: number,
    options: OpportunityResolutionOptions = {}
  ): Promise<OpportunityResolution> {
    const { customFields, fallbackToSalesforce = true } = options;

    // Strategy 1: Check custom fields
    if (customFields) {
      const opportunityId = this.extractOpportunityIdFromCustomFields(customFields);
      if (opportunityId) {
        return {
          opportunityId,
          source: "custom_fields",
        };
      }
    }

    // Strategy 2: Salesforce lookup by WHMCS Service ID
    if (fallbackToSalesforce) {
      try {
        const opportunityId =
          await this.opportunityService.findOpportunityByWhmcsServiceId(subscriptionId);
        if (opportunityId) {
          return {
            opportunityId,
            source: "salesforce_lookup",
          };
        }
      } catch {
        // Opportunity lookup failure is non-critical
        this.logger.warn("Could not find Opportunity for subscription", { subscriptionId });
      }
    }

    return {
      opportunityId: null,
      source: "not_found",
    };
  }

  // ============================================================================
  // Private Helpers
  // ============================================================================

  /**
   * Extract OpportunityId from WHMCS custom fields.
   *
   * Checks common key variations:
   * - OpportunityId, OpportunityID, opportunityId, opportunityID
   * - Falls back to case-insensitive scan for "opportunity" + "id"
   */
  private extractOpportunityIdFromCustomFields(
    customFields: Record<string, string>
  ): string | undefined {
    // Prefer exact key (as configured in WHMCS)
    const direct =
      customFields["OpportunityId"] ??
      customFields["OpportunityID"] ??
      customFields["opportunityId"] ??
      customFields["opportunityID"];

    if (direct && direct.trim().length > 0) {
      return direct.trim();
    }

    // Fallback: case-insensitive scan for something like "Opportunity Id"
    const entry = Object.entries(customFields).find(([key, value]) => {
      if (!value) return false;
      const k = key.toLowerCase();
      return k.includes("opportunity") && k.replace(/\s+/g, "").includes("id");
    });

    return entry?.[1]?.trim() || undefined;
  }
}
