import { Injectable, BadRequestException, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { SalesforceService } from "@bff/integrations/salesforce/salesforce.service";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import {
  OrderFulfillmentValidationResult,
  sfOrderIdParamSchema,
} from "@customer-portal/domain/orders";
import { salesforceAccountIdSchema } from "@customer-portal/domain/common";
import type { SalesforceOrderRecord } from "@customer-portal/domain/orders/providers/salesforce/raw.types";
import { PaymentValidatorService } from "./payment-validator.service";

/**
 * Handles all order fulfillment validation logic
 * Similar to OrderValidator but for fulfillment workflow
 */
@Injectable()
export class OrderFulfillmentValidator {
  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly salesforceService: SalesforceService,
    private readonly mappingsService: MappingsService,
    private readonly paymentValidator: PaymentValidatorService
  ) {}

  /**
   * Complete validation for fulfillment request
   * Validates SF order, gets client ID, checks payment method, checks idempotency
   */
  async validateFulfillmentRequest(
    rawSfOrderId: string,
    idempotencyKey: string
  ): Promise<OrderFulfillmentValidationResult> {
    const { sfOrderId } = sfOrderIdParamSchema.parse({ sfOrderId: rawSfOrderId });

    this.logger.log("Starting fulfillment validation", {
      sfOrderId,
      idempotencyKey,
    });

    try {
      // 1. Validate Salesforce order exists and get details
      const sfOrder = await this.validateSalesforceOrder(sfOrderId);

      // 2. Check if already provisioned (idempotency)

      const existingWhmcsOrderId = sfOrder.WHMCS_Order_ID__c;
      if (existingWhmcsOrderId) {
        this.logger.log("Order already provisioned", {
          sfOrderId,
          whmcsOrderId: existingWhmcsOrderId,
        });

        return {
          sfOrder,
          clientId: 0, // Not needed for already provisioned
          isAlreadyProvisioned: true,
          whmcsOrderId: existingWhmcsOrderId,
        };
      }

      // 3. Get WHMCS client mapping
      // Validate AccountId using schema instead of manual type checks
      const accountId = salesforceAccountIdSchema.parse(sfOrder.AccountId);
      const mapping = await this.mappingsService.findBySfAccountId(accountId);
      if (!mapping?.whmcsClientId) {
        throw new BadRequestException(`No WHMCS client mapping found for account ${accountId}`);
      }
      const clientId = mapping.whmcsClientId;

      // 4. Validate payment method exists
      await this.validatePaymentMethod(clientId, mapping.userId);

      this.logger.log("Fulfillment validation completed successfully", {
        sfOrderId,
        clientId,
        accountId,
      });

      return {
        sfOrder,
        clientId,
        isAlreadyProvisioned: false,
      };
    } catch (error) {
      this.logger.error("Fulfillment validation failed", {
        error: getErrorMessage(error),
        sfOrderId,
        idempotencyKey,
      });
      throw error;
    }
  }

  /**
   * Validate Salesforce order exists and is in valid state
   */
  private async validateSalesforceOrder(sfOrderId: string): Promise<SalesforceOrderRecord> {
    const order = await this.salesforceService.getOrder(sfOrderId);

    if (!order) {
      throw new BadRequestException(`Salesforce order ${sfOrderId} not found`);
    }

    if (order.Status === "Cancelled") {
      throw new BadRequestException(`Cannot provision cancelled order ${sfOrderId}`);
    }

    this.logger.log("Salesforce order validated", {
      sfOrderId,
      status: order.Status,
      activationStatus: order.Activation_Status__c,
      accountId: order.AccountId,
    });

    return order;
  }

  /**
   * Validate client has payment method in WHMCS
   * Delegates to PaymentValidatorService for consistent validation
   */
  private async validatePaymentMethod(clientId: number, userId: string): Promise<void> {
    return this.paymentValidator.validatePaymentMethodExists(userId, clientId);
  }
}
