import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { WhmcsConnectionFacade } from "../facades/whmcs.facade.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { WhmcsOperationException } from "@bff/core/exceptions/domain-exceptions.js";

import {
  buildWhmcsAddOrderPayload,
  whmcsAddOrderResponseSchema,
  whmcsAcceptOrderResponseSchema,
  type WhmcsOrderItem,
  type WhmcsAddOrderParams,
  type WhmcsAddOrderResponse,
  type WhmcsOrderResult,
  type WhmcsAddOrderPayload,
  type WhmcsAcceptOrderResponse,
} from "@customer-portal/domain/orders/providers";

export type { WhmcsOrderItem, WhmcsAddOrderParams, WhmcsOrderResult };

/** Raw WHMCS order record from GetOrders API */
interface WhmcsGetOrdersOrder {
  id?: string | number;
  ordernum?: string;
  date?: string;
  status?: string;
  invoiceid?: string | number;
  [key: string]: unknown;
}

/** Raw WHMCS GetOrders API response */
interface WhmcsGetOrdersResponse {
  orders?: {
    order?: WhmcsGetOrdersOrder[];
  };
  totalresults?: number;
  [key: string]: unknown;
}

@Injectable()
export class WhmcsOrderService {
  constructor(
    private readonly connection: WhmcsConnectionFacade,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Create order in WHMCS using AddOrder API
   * Maps Salesforce OrderItems to WHMCS products
   *
   * WHMCS API Response Structure:
   * Success: { orderid, productids, serviceids, addonids, domainids, invoiceid }
   * Error: Thrown by HTTP client before returning
   */
  async addOrder(params: WhmcsAddOrderParams): Promise<WhmcsOrderResult> {
    this.logger.log("Creating WHMCS order", {
      clientId: params.clientId,
      itemCount: params.items.length,
      sfOrderId: params.sfOrderId,
      hasPromoCode: Boolean(params.promoCode),
    });

    try {
      // Build WHMCS AddOrder payload
      const addOrderPayload = this.buildAddOrderPayload(params);

      this.logger.debug("Built WHMCS AddOrder payload", {
        clientId: params.clientId,
        productCount: addOrderPayload.pid.length,
        pids: addOrderPayload.pid,
        quantities: addOrderPayload.qty, // CRITICAL: Must be included for products to be added
        billingCycles: addOrderPayload.billingcycle,
        hasConfigOptions: Boolean(addOrderPayload.configoptions),
        hasCustomFields: Boolean(addOrderPayload.customfields),
        promoCode: addOrderPayload.promocode,
        paymentMethod: addOrderPayload.paymentmethod,
      });

      // Call WHMCS AddOrder API
      // Note: The HTTP client throws errors automatically if result === "error"
      // So we only get here if the request was successful
      const response = (await this.connection.addOrder(addOrderPayload)) as WhmcsAddOrderResponse;

      // Log the full response for debugging
      this.logger.debug("WHMCS AddOrder response", {
        response,
        clientId: params.clientId,
        sfOrderId: params.sfOrderId,
      });

      const parsedResponse = whmcsAddOrderResponseSchema.safeParse(response);
      if (!parsedResponse.success) {
        this.logger.error("WHMCS AddOrder response failed validation", {
          clientId: params.clientId,
          sfOrderId: params.sfOrderId,
          issues: parsedResponse.error.flatten(),
          rawResponse: response,
        });
        throw new WhmcsOperationException("WHMCS AddOrder response was invalid", {
          response,
        });
      }

      const normalizedResult = this.toWhmcsOrderResult(parsedResponse.data);

      this.logger.log("WHMCS order created successfully", {
        orderId: normalizedResult.orderId,
        invoiceId: normalizedResult.invoiceId,
        serviceIds: normalizedResult.serviceIds,
        addonIds: normalizedResult.addonIds,
        domainIds: normalizedResult.domainIds,
        clientId: params.clientId,
        sfOrderId: params.sfOrderId,
      });

      return normalizedResult;
    } catch (error) {
      // Enhanced error logging with full context
      this.logger.error("Failed to create WHMCS order", {
        error: extractErrorMessage(error),
        errorType: error?.constructor?.name,
        clientId: params.clientId,
        sfOrderId: params.sfOrderId,
        itemCount: params.items.length,
        // Include first 100 chars of error stack for debugging
        errorStack: error instanceof Error ? error.stack?.slice(0, 100) : undefined,
      });
      throw error;
    }
  }

  /**
   * Accept/provision order in WHMCS using AcceptOrder API
   * This activates services and creates subscriptions
   *
   * WHMCS API Response Structure:
   * Success: { orderid, invoiceid, serviceids, addonids, domainids }
   * Error: Thrown by HTTP client before returning
   *
   * @returns Service IDs created by AcceptOrder (services are created on accept, not on add)
   */
  async acceptOrder(
    orderId: number,
    sfOrderId?: string
  ): Promise<{ serviceIds: number[]; invoiceId?: number }> {
    this.logger.log("Accepting WHMCS order", {
      orderId,
      sfOrderId,
    });

    try {
      // Call WHMCS AcceptOrder API
      // Note: The HTTP client throws errors automatically if result === "error"
      // So we only get here if the request was successful
      const response = (await this.connection.acceptOrder(orderId)) as WhmcsAcceptOrderResponse;

      // Log the full response for debugging
      this.logger.debug("WHMCS AcceptOrder response", {
        response,
        orderId,
        sfOrderId,
      });

      const parsedResponse = whmcsAcceptOrderResponseSchema.safeParse(response);
      if (!parsedResponse.success) {
        this.logger.error("WHMCS AcceptOrder response failed validation", {
          orderId,
          sfOrderId,
          issues: parsedResponse.error.flatten(),
          rawResponse: response,
        });
        throw new WhmcsOperationException("WHMCS AcceptOrder response was invalid", {
          response,
        });
      }

      const serviceIds = this.parseDelimitedIds(parsedResponse.data.serviceids);
      const invoiceId = parsedResponse.data.invoiceid
        ? Number.parseInt(String(parsedResponse.data.invoiceid), 10)
        : undefined;

      this.logger.log("WHMCS order accepted successfully", {
        orderId,
        invoiceId,
        serviceIds,
        sfOrderId,
      });

      return { serviceIds, ...(invoiceId !== undefined && { invoiceId }) };
    } catch (error) {
      // Enhanced error logging with full context
      this.logger.error("Failed to accept WHMCS order", {
        error: extractErrorMessage(error),
        errorType: error?.constructor?.name,
        orderId,
        sfOrderId,
        // Include first 100 chars of error stack for debugging
        errorStack: error instanceof Error ? error.stack?.slice(0, 100) : undefined,
      });
      throw error;
    }
  }

  /**
   * Update service custom fields after order is accepted
   * Uses UpdateClientProduct API which supports field names (not just IDs)
   *
   * @param serviceId - The WHMCS service ID to update
   * @param customFields - Key-value map of custom field names to values
   */
  async updateServiceCustomFields(
    serviceId: number,
    customFields: Record<string, string>
  ): Promise<void> {
    if (!customFields || Object.keys(customFields).length === 0) {
      return;
    }

    this.logger.log("Updating WHMCS service custom fields", {
      serviceId,
      fieldCount: Object.keys(customFields).length,
      fieldNames: Object.keys(customFields),
    });

    try {
      // Serialize custom fields to WHMCS format (base64 encoded PHP serialized array)
      const serialized = this.serializeCustomFields(customFields);

      await this.connection.makeRequest("UpdateClientProduct", {
        serviceid: serviceId,
        customfields: serialized,
      });

      this.logger.log("WHMCS service custom fields updated", {
        serviceId,
        fields: Object.keys(customFields),
      });
    } catch (error) {
      this.logger.error("Failed to update WHMCS service custom fields", {
        error: extractErrorMessage(error),
        serviceId,
        fieldNames: Object.keys(customFields),
      });
      throw error;
    }
  }

  /**
   * Serialize custom fields to WHMCS format (base64 encoded PHP serialized array)
   */
  private serializeCustomFields(data: Record<string, string>): string {
    const entries = Object.entries(data).filter(([k, v]) => k && v);
    if (entries.length === 0) return "";

    const parts = entries.map(([key, value]) => {
      const keyBytes = Buffer.byteLength(key, "utf8");
      const valueBytes = Buffer.byteLength(value, "utf8");
      return `s:${keyBytes}:"${key}";s:${valueBytes}:"${value}";`;
    });

    const serialized = `a:${entries.length}:{${parts.join("")}}`;
    return Buffer.from(serialized).toString("base64");
  }

  /**
   * Get order details from WHMCS
   */
  async getOrderDetails(orderId: number): Promise<WhmcsGetOrdersOrder | null> {
    try {
      // Note: The HTTP client throws errors automatically if result === "error"
      const response = (await this.connection.getOrders({
        id: orderId.toString(),
      })) as WhmcsGetOrdersResponse;

      return response.orders?.order?.[0] ?? null;
    } catch (error) {
      this.logger.error("Failed to get WHMCS order details", {
        error: extractErrorMessage(error),
        errorType: error?.constructor?.name,
        orderId,
      });
      throw error;
    }
  }

  /**
   * Check if client has valid payment method
   */
  async hasPaymentMethod(clientId: number): Promise<boolean> {
    try {
      const response = await this.connection.getPaymentMethods({
        clientid: clientId,
      });

      const methods = Array.isArray(response.paymethods) ? response.paymethods : [];
      const hasValidMethod = methods.length > 0;

      this.logger.log("Payment method check completed", {
        clientId,
        hasPaymentMethod: hasValidMethod,
        methodCount: methods.length,
      });

      return hasValidMethod;
    } catch (error) {
      this.logger.error("Failed to check payment methods", {
        error: extractErrorMessage(error),
        clientId,
      });
      // Don't throw - return false to indicate no payment method
      return false;
    }
  }

  /**
   * Build WHMCS AddOrder payload from our parameters
   * Following official WHMCS API documentation format
   *
   * Delegates to shared mapper function from integration package
   */
  private buildAddOrderPayload(params: WhmcsAddOrderParams): WhmcsAddOrderPayload {
    const payload = buildWhmcsAddOrderPayload(params);

    this.logger.debug("Built WHMCS AddOrder payload", {
      clientId: params.clientId,
      productCount: params.items.length,
      pids: payload.pid,
      billingCycles: payload.billingcycle,
      hasConfigOptions: !!payload.configoptions,
      hasCustomFields: !!payload.customfields,
    });

    return payload;
  }

  private toWhmcsOrderResult(response: WhmcsAddOrderResponse): WhmcsOrderResult {
    const orderId = Number.parseInt(String(response.orderid), 10);
    if (!orderId || Number.isNaN(orderId)) {
      throw new WhmcsOperationException("WHMCS AddOrder did not return valid order ID", {
        response,
      });
    }

    return {
      orderId,
      invoiceId: response.invoiceid ? Number.parseInt(String(response.invoiceid), 10) : undefined,
      serviceIds: this.parseDelimitedIds(response.serviceids),
      addonIds: this.parseDelimitedIds(response.addonids),
      domainIds: this.parseDelimitedIds(response.domainids),
    };
  }

  private parseDelimitedIds(value?: string): number[] {
    if (!value) {
      return [];
    }
    return value
      .toString()
      .split(",")
      .map(entry => Number.parseInt(entry.trim(), 10))
      .filter(id => !Number.isNaN(id));
  }
}
