import { Injectable, Inject, NotFoundException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { SalesforceOrderService } from "@bff/integrations/salesforce/services/salesforce-order.service";
import { OrderValidator } from "./order-validator.service";
import { OrderBuilder } from "./order-builder.service";
import { OrderItemBuilder, OrderItemCompositePayload } from "./order-item-builder.service";
import { OrdersCacheService } from "./orders-cache.service";
import { type OrderDetails, type OrderSummary } from "@customer-portal/domain/orders";
import { assertSalesforceId } from "@bff/integrations/salesforce/utils/soql.util";

type OrderDetailsResponse = OrderDetails;
type OrderSummaryResponse = OrderSummary;

/**
 * Main orchestrator for order operations
 * Coordinates all order-related services
 */
@Injectable()
export class OrderOrchestrator {
  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly salesforceOrderService: SalesforceOrderService,
    private readonly orderValidator: OrderValidator,
    private readonly orderBuilder: OrderBuilder,
    private readonly orderItemBuilder: OrderItemBuilder,
    private readonly ordersCache: OrdersCacheService
  ) {}

  /**
   * Create a new order - main entry point
   */
  async createOrder(userId: string, rawBody: unknown) {
    this.logger.log({ userId }, "Order creation workflow started");

    // 1) Complete validation (format + business rules)
    const { validatedBody, userMapping, pricebookId } =
      await this.orderValidator.validateCompleteOrder(userId, rawBody);

    this.logger.log(
      {
        userId,
        orderType: validatedBody.orderType,
        skuCount: validatedBody.skus.length,
      },
      "Order validation completed successfully"
    );

    // 2) Build order fields (includes address snapshot)
    const orderFields = await this.orderBuilder.buildOrderFields(
      validatedBody,
      userMapping,
      pricebookId,
      validatedBody.userId
    );

    const orderItemsPayload: OrderItemCompositePayload[] =
      await this.orderItemBuilder.buildOrderItemsPayload(validatedBody.skus, pricebookId);

    this.logger.log(
      {
        userId,
        orderType: validatedBody.orderType,
        skuCount: validatedBody.skus.length,
        orderItemCount: orderItemsPayload.length,
      },
      "Order payload prepared"
    );

    const created = await this.salesforceOrderService.createOrderWithItems(
      orderFields,
      orderItemsPayload
    );

    if (userMapping.sfAccountId) {
      await this.ordersCache.invalidateAccountOrders(userMapping.sfAccountId);
    }
    await this.ordersCache.invalidateOrder(created.id);

    this.logger.log(
      {
        orderId: created.id,
        skuCount: validatedBody.skus.length,
        orderItemCount: orderItemsPayload.length,
      },
      "Order creation workflow completed successfully"
    );

    return {
      sfOrderId: created.id,
      status: "Created",
      message: "Order created successfully in Salesforce",
    };
  }

  /**
   * Get order by ID with order items
   */
  async getOrder(orderId: string): Promise<OrderDetailsResponse | null> {
    const safeOrderId = assertSalesforceId(orderId, "orderId");
    this.logger.log({ orderId: safeOrderId }, "Fetching order details");

    // Use integration service - it handles queries and transformations
    return this.ordersCache.getOrderDetails(safeOrderId, () =>
      this.salesforceOrderService.getOrderById(safeOrderId)
    );
  }

  /**
   * Get order scoped to authenticated user account
   */
  async getOrderForUser(orderId: string, userId: string): Promise<OrderDetailsResponse> {
    const userMapping = await this.orderValidator.validateUserMapping(userId);
    const sfAccountId = userMapping.sfAccountId
      ? assertSalesforceId(userMapping.sfAccountId, "sfAccountId")
      : null;

    if (!sfAccountId) {
      this.logger.warn({ userId }, "User mapping missing Salesforce account ID");
      throw new NotFoundException("Order not found");
    }

    const safeOrderId = assertSalesforceId(orderId, "orderId");
    const order = await this.getOrder(safeOrderId);
    if (!order) {
      throw new NotFoundException("Order not found");
    }

    if (!order.accountId || order.accountId !== sfAccountId) {
      this.logger.warn(
        {
          userId,
          orderId: safeOrderId,
          orderAccountId: order.accountId,
          expectedAccountId: sfAccountId,
        },
        "Order access denied due to account mismatch"
      );
      throw new NotFoundException("Order not found");
    }

    return order;
  }

  /**
   * Get orders for a user with basic item summary
   */
  async getOrdersForUser(userId: string): Promise<OrderSummaryResponse[]> {
    this.logger.log({ userId }, "Fetching user orders");

    // Get user mapping
    const userMapping = await this.orderValidator.validateUserMapping(userId);
    const sfAccountId = userMapping.sfAccountId
      ? assertSalesforceId(userMapping.sfAccountId, "sfAccountId")
      : undefined;

    if (!sfAccountId) {
      this.logger.warn({ userId }, "User mapping missing Salesforce account ID");
      return [];
    }

    // Use integration service - it handles queries and transformations
    return this.ordersCache.getOrderSummaries(sfAccountId, () =>
      this.salesforceOrderService.getOrdersForAccount(sfAccountId)
    );
  }
}
