import { Injectable, Inject, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import {
  CheckoutCart,
  CheckoutItem,
  CheckoutTotals,
  checkoutCartSchema,
  OrderConfigurations,
  ORDER_TYPE,
  type OrderTypeValue,
  type OrderSelections,
  buildOrderConfigurations,
} from "@customer-portal/domain/orders";
import type {
  InternetPlanCatalogItem,
  InternetAddonCatalogItem,
  InternetInstallationCatalogItem,
  SimCatalogProduct,
  SimActivationFeeCatalogItem,
  VpnCatalogProduct,
} from "@customer-portal/domain/catalog";
import { InternetCatalogService } from "@bff/modules/catalog/services/internet-catalog.service";
import { SimCatalogService } from "@bff/modules/catalog/services/sim-catalog.service";
import { VpnCatalogService } from "@bff/modules/catalog/services/vpn-catalog.service";
import { getErrorMessage } from "@bff/core/utils/error.util";

@Injectable()
export class CheckoutService {
  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly internetCatalogService: InternetCatalogService,
    private readonly simCatalogService: SimCatalogService,
    private readonly vpnCatalogService: VpnCatalogService
  ) {}

  /**
   * Build checkout cart from order type and selections
   */
  async buildCart(
    orderType: OrderTypeValue,
    selections: OrderSelections,
    configuration?: OrderConfigurations,
    userId?: string
  ): Promise<CheckoutCart> {
    this.logger.log("Building checkout cart", { orderType, selections });

    try {
      const items: CheckoutItem[] = [];
      let totals: CheckoutTotals = { monthlyTotal: 0, oneTimeTotal: 0 };

      if (orderType === ORDER_TYPE.INTERNET) {
        const cart = await this.buildInternetCart(selections, userId);
        items.push(...cart.items);
        totals = this.calculateTotals(items);
      } else if (orderType === ORDER_TYPE.SIM) {
        const cart = await this.buildSimCart(selections, userId);
        items.push(...cart.items);
        totals = this.calculateTotals(items);
      } else if (orderType === ORDER_TYPE.VPN) {
        const cart = await this.buildVpnCart(selections);
        items.push(...cart.items);
        totals = this.calculateTotals(items);
      } else {
        throw new BadRequestException(`Unsupported order type: ${orderType}`);
      }

      const configurationPayload: OrderConfigurations =
        configuration ?? buildOrderConfigurations(selections);

      const cart: CheckoutCart = {
        items,
        totals,
        configuration: configurationPayload,
      };

      // Validate the cart using domain schema
      const validatedCart = checkoutCartSchema.parse(cart);

      this.logger.log("Checkout cart built successfully", {
        itemCount: validatedCart.items.length,
        items: validatedCart.items.map(item => ({
          sku: item.sku,
          name: item.name,
          itemType: item.itemType,
        })),
        monthlyTotal: validatedCart.totals.monthlyTotal,
        oneTimeTotal: validatedCart.totals.oneTimeTotal,
      });

      return validatedCart;
    } catch (error) {
      this.logger.error("Failed to build checkout cart", {
        error: getErrorMessage(error),
        orderType,
        selections,
      });
      throw error;
    }
  }

  /**
   * Calculate totals for checkout items
   */
  calculateTotals(items: CheckoutItem[]): CheckoutTotals {
    return items.reduce<CheckoutTotals>(
      (acc, item) => {
        if (typeof item.monthlyPrice === "number") {
          acc.monthlyTotal += item.monthlyPrice * item.quantity;
        }
        if (typeof item.oneTimePrice === "number") {
          acc.oneTimeTotal += item.oneTimePrice * item.quantity;
        }
        return acc;
      },
      { monthlyTotal: 0, oneTimeTotal: 0 }
    );
  }

  /**
   * Validate checkout cart for business rules
   */
  validateCart(cart: CheckoutCart): void {
    this.logger.log("Validating checkout cart", { itemCount: cart.items.length });

    // Validate cart structure using domain schema
    checkoutCartSchema.parse(cart);

    // Business validation rules
    if (cart.items.length === 0) {
      throw new BadRequestException("Cart cannot be empty");
    }

    // Validate SKUs exist in catalog
    for (const item of cart.items) {
      this.validateSkuExists(item.sku);
    }

    // Validate quantities
    for (const item of cart.items) {
      if (item.quantity <= 0) {
        throw new BadRequestException(`Invalid quantity for ${item.sku}: ${item.quantity}`);
      }
    }

    this.logger.log("Checkout cart validation passed");
  }

  /**
   * Build Internet order cart
   */
  private async buildInternetCart(
    selections: OrderSelections,
    userId?: string
  ): Promise<{ items: CheckoutItem[] }> {
    const items: CheckoutItem[] = [];
    const plans: InternetPlanCatalogItem[] = userId
      ? await this.internetCatalogService.getPlansForUser(userId)
      : await this.internetCatalogService.getPlans();
    const addons: InternetAddonCatalogItem[] = await this.internetCatalogService.getAddons();
    const installations: InternetInstallationCatalogItem[] =
      await this.internetCatalogService.getInstallations();

    // Add main plan
    if (!selections.planSku) {
      throw new BadRequestException("No plan selected for Internet order");
    }

    const plan = plans.find(p => p.sku === selections.planSku);
    if (!plan) {
      throw new BadRequestException(`Internet plan not found: ${selections.planSku}`);
    }

    items.push({
      id: plan.id,
      sku: plan.sku,
      name: plan.name,
      description: plan.description,
      monthlyPrice: plan.monthlyPrice,
      oneTimePrice: plan.oneTimePrice,
      quantity: 1,
      itemType: "plan",
    });

    // Add installation if selected
    if (selections.installationSku) {
      const installation = installations.find(inst => inst.sku === selections.installationSku);
      if (installation) {
        items.push({
          id: installation.id,
          sku: installation.sku,
          name: installation.name,
          description: installation.description,
          monthlyPrice: installation.monthlyPrice,
          oneTimePrice: installation.oneTimePrice,
          quantity: 1,
          itemType: "installation",
        });
      }
    }

    // Add addons
    const addonRefs = this.collectAddonRefs(selections);
    for (const ref of addonRefs) {
      const addon = addons.find(a => a.sku === ref || a.id === ref);
      if (addon) {
        items.push({
          id: addon.id,
          sku: addon.sku,
          name: addon.name,
          description: addon.description,
          monthlyPrice: addon.monthlyPrice,
          oneTimePrice: addon.oneTimePrice,
          quantity: 1,
          itemType: "addon",
        });
      }
    }

    return { items };
  }

  /**
   * Build SIM order cart
   */
  private async buildSimCart(
    selections: OrderSelections,
    userId?: string
  ): Promise<{ items: CheckoutItem[] }> {
    const items: CheckoutItem[] = [];
    const plans: SimCatalogProduct[] = userId
      ? await this.simCatalogService.getPlansForUser(userId)
      : await this.simCatalogService.getPlans();
    const rawActivationFees: SimActivationFeeCatalogItem[] =
      await this.simCatalogService.getActivationFees();
    const activationFees = this.filterActivationFeesWithSku(rawActivationFees);
    const addons: SimCatalogProduct[] = await this.simCatalogService.getAddons();

    // Add main plan
    if (!selections.planSku) {
      throw new BadRequestException("No plan selected for SIM order");
    }

    const plan = plans.find(p => p.sku === selections.planSku);
    if (!plan) {
      throw new BadRequestException(`SIM plan not found: ${selections.planSku}`);
    }

    items.push({
      id: plan.id,
      sku: plan.sku,
      name: plan.name,
      description: plan.description,
      monthlyPrice: plan.monthlyPrice,
      oneTimePrice: plan.oneTimePrice,
      quantity: 1,
      itemType: "plan",
    });

    // Add activation fee (required)
    this.logger.debug("Activation fees available", {
      rawActivationFeeCount: rawActivationFees.length,
      sanitizedActivationFeeCount: activationFees.length,
      activationFees: activationFees.map(f => ({ id: f.id, sku: f.sku, name: f.name })),
    });
    
    const activationSelection = this.resolveSimActivationFee(activationFees, selections);
    if (!activationSelection) {
      this.logger.error("SIM activation fee is not available", {
        planSku: selections.planSku,
        rawActivationFeeCount: rawActivationFees.length,
        sanitizedActivationFeeCount: activationFees.length,
      });
      throw new BadRequestException("SIM activation fee is not available");
    }

    const { fee: activation, autoAdded } = activationSelection;
    this.logger.debug("Adding activation fee to cart", {
      id: activation.id,
      sku: activation.sku,
      name: activation.name,
      autoAdded,
    });
    
    items.push({
      id: activation.id,
      sku: activation.sku,
      name: activation.name,
      description: activation.description,
      monthlyPrice: activation.monthlyPrice,
      oneTimePrice: activation.oneTimePrice,
      quantity: 1,
      itemType: "activation",
      autoAdded,
    });

    // Add addons
    const addonRefs = this.collectAddonRefs(selections);
    for (const ref of addonRefs) {
      const addon = addons.find(a => a.sku === ref || a.id === ref);
      if (addon) {
        items.push({
          id: addon.id,
          sku: addon.sku,
          name: addon.name,
          description: addon.description,
          monthlyPrice: addon.monthlyPrice,
          oneTimePrice: addon.oneTimePrice,
          quantity: 1,
          itemType: "addon",
        });
      }
    }

    return { items };
  }

  /**
   * Build VPN order cart
   */
  private async buildVpnCart(selections: OrderSelections): Promise<{ items: CheckoutItem[] }> {
    const items: CheckoutItem[] = [];
    const plans: VpnCatalogProduct[] = await this.vpnCatalogService.getPlans();
    const activationFees: VpnCatalogProduct[] = await this.vpnCatalogService.getActivationFees();

    // Add main plan
    if (!selections.planSku) {
      throw new BadRequestException("No plan selected for VPN order");
    }

    const plan = plans.find(p => p.sku === selections.planSku);
    if (!plan) {
      throw new BadRequestException(`VPN plan not found: ${selections.planSku}`);
    }

    items.push({
      id: plan.id,
      sku: plan.sku,
      name: plan.name,
      description: plan.description,
      monthlyPrice: plan.monthlyPrice,
      oneTimePrice: plan.oneTimePrice,
      quantity: 1,
      itemType: "vpn",
    });

    // Add activation fee if selected
    if (selections.activationSku) {
      const activation = activationFees.find(fee => fee.sku === selections.activationSku);
      if (activation) {
        items.push({
          id: activation.id,
          sku: activation.sku,
          name: activation.name,
          description: activation.description,
          monthlyPrice: activation.monthlyPrice,
          oneTimePrice: activation.oneTimePrice,
          quantity: 1,
          itemType: "activation",
        });
      }
    }

    return { items };
  }

  /**
   * Resolve the correct SIM activation fee to include in the cart
   */
  private filterActivationFeesWithSku(
    activationFees: SimActivationFeeCatalogItem[]
  ): SimActivationFeeCatalogItem[] {
    return activationFees.filter(fee => {
      const hasSku = typeof fee.sku === "string" && fee.sku.trim().length > 0;
      if (!hasSku) {
        this.logger.warn("Skipping SIM activation fee without SKU", {
          activationId: fee.id,
          name: fee.name,
        });
      }
      return hasSku;
    });
  }

  private resolveSimActivationFee(
    activationFees: SimActivationFeeCatalogItem[],
    selections: OrderSelections
  ): { fee: SimActivationFeeCatalogItem; autoAdded: boolean } | null {
    if (!Array.isArray(activationFees) || activationFees.length === 0) {
      return null;
    }

    const findByRef = (ref?: string | null): SimActivationFeeCatalogItem | undefined => {
      if (!ref) {
        return undefined;
      }
      return activationFees.find(fee => fee.sku === ref || fee.id === ref);
    };

    const explicitFee = findByRef(selections.activationFeeSku);
    if (explicitFee) {
      return { fee: explicitFee, autoAdded: false };
    }

    const autoAddFee = activationFees.find(fee => fee.catalogMetadata?.autoAdd);
    if (autoAddFee) {
      return { fee: autoAddFee, autoAdded: true };
    }

    const defaultFee = activationFees.find(fee => fee.catalogMetadata?.isDefault);
    if (defaultFee) {
      return { fee: defaultFee, autoAdded: true };
    }

    return { fee: activationFees[0], autoAdded: true };
  }

  /**
   * Collect addon references from selections
   */
  private collectAddonRefs(selections: OrderSelections): string[] {
    const refs = new Set<string>();

    // Handle various addon selection formats
    if (selections.addonSku) refs.add(selections.addonSku);
    if (selections.addons) {
      selections.addons
        .split(",")
        .map(value => value.trim())
        .filter(Boolean)
        .forEach(value => refs.add(value));
    }

    return Array.from(refs);
  }

  /**
   * Validate that SKU exists in catalog
   */
  private validateSkuExists(sku: string): void {
    // This would typically check against the catalog service
    // For now, we'll do a basic validation
    if (!sku || sku.trim().length === 0) {
      throw new BadRequestException("Invalid SKU: empty or null");
    }
  }
}
