import { Injectable, Inject } from "@nestjs/common";
import type { OnModuleInit } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { SalesforceConnection } from "../services/salesforce-connection.service.js";
import {
  SalesforceAccountService,
  type SalesforceAccountPortalUpdate,
  type UpdateSalesforceContactAddressRequest,
} from "../services/salesforce-account.service.js";
import { SalesforceOperationException } from "@bff/core/exceptions/domain-exceptions.js";
import type { SalesforceOrderRecord } from "@customer-portal/domain/orders/providers";

/**
 * Salesforce Facade
 *
 * Unified entry point for Salesforce operations used across the BFF.
 *
 * @remarks
 * Account Methods:
 * - findAccountByCustomerNumber() - Used in signup/WHMCS linking workflows
 * - getAccountDetails() - Used in signup to check WH_Account__c field
 *
 * Order Methods:
 * - updateOrder() - Used in order provisioning
 * - getOrder() - Used to fetch order details
 *
 * Note: For complex queries, use specialized services (SalesforceOrderService, etc.)
 */
@Injectable()
export class SalesforceFacade implements OnModuleInit {
  constructor(
    private configService: ConfigService,
    private connection: SalesforceConnection,
    private accountService: SalesforceAccountService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  async onModuleInit() {
    try {
      await this.connection.connect();
      if (!this.connection.isConnected()) {
        this.logger.warn(
          "Salesforce connection is not established. Running without Salesforce integration."
        );
        return;
      }
    } catch (error) {
      const nodeEnv =
        this.configService.get<string>("NODE_ENV") || process.env["NODE_ENV"] || "development";
      const isProd = nodeEnv === "production";
      if (isProd) {
        this.logger.error("Failed to initialize Salesforce connection");
      } else {
        this.logger.error(
          `Failed to initialize Salesforce connection: ${extractErrorMessage(error)}`
        );
      }
    }
  }

  // === ACCOUNT METHODS (Actually Used) ===

  async findAccountByCustomerNumber(customerNumber: string): Promise<{ id: string } | null> {
    return this.accountService.findByCustomerNumber(customerNumber);
  }

  async findAccountWithDetailsByCustomerNumber(customerNumber: string): Promise<{
    id: string;
    WH_Account__c?: string | null | undefined;
    Name?: string | null | undefined;
  } | null> {
    return this.accountService.findWithDetailsByCustomerNumber(customerNumber);
  }

  async getAccountDetails(accountId: string): Promise<{
    id: string;
    WH_Account__c?: string | null | undefined;
    Name?: string | null | undefined;
  } | null> {
    return this.accountService.getAccountDetails(accountId);
  }

  async updateAccountPortalFields(
    accountId: string,
    update: SalesforceAccountPortalUpdate
  ): Promise<void> {
    await this.accountService.updatePortalFields(accountId, update);
  }

  /**
   * Update PersonContact address fields for dual-write sync
   * Stores Japanese address data in Salesforce
   */
  async updateContactAddress(
    accountId: string,
    address: UpdateSalesforceContactAddressRequest
  ): Promise<void> {
    await this.accountService.updateContactAddress(accountId, address);
  }

  // === ORDER METHODS (For Order Provisioning) ===

  async updateOrder(orderData: Partial<SalesforceOrderRecord> & { Id: string }): Promise<void> {
    const orderId = orderData.Id;
    try {
      if (!this.connection.isConnected()) {
        throw new SalesforceOperationException("Salesforce connection not available", {
          operation: "updateOrder",
          orderId,
        });
      }

      const sobject = this.connection.sobject("Order");
      if (!sobject) {
        throw new SalesforceOperationException("Failed to get Salesforce Order sobject", {
          operation: "updateOrder",
          orderId,
        });
      }

      if (sobject.update) {
        await sobject.update(orderData);
      } else {
        throw new SalesforceOperationException(
          "Salesforce Order sobject does not support update operation",
          {
            operation: "updateOrder",
            orderId,
          }
        );
      }

      this.logger.log("Order updated in Salesforce", {
        orderId: orderData.Id,
        fields: Object.keys(orderData).filter(k => k !== "Id"),
      });
    } catch (error) {
      this.logger.error("Failed to update order in Salesforce", {
        orderId: orderData.Id,
        error: extractErrorMessage(error),
      });
      throw error;
    }
  }

  async getOrder(orderId: string): Promise<SalesforceOrderRecord | null> {
    try {
      if (!this.connection.isConnected()) {
        throw new SalesforceOperationException("Salesforce connection not available", {
          operation: "getOrder",
          orderId,
        });
      }

      const result = (await this.connection.query(
        `SELECT Id, Status, Type, Activation_Status__c, WHMCS_Order_ID__c, 
                Activation_Error_Code__c, Activation_Error_Message__c,
                AccountId, Account.Name
         FROM Order 
         WHERE Id = '${orderId}' 
         LIMIT 1`,
        { label: "orders:integration:getOrder" }
      )) as { records: SalesforceOrderRecord[]; totalSize: number };

      return result.records?.[0] || null;
    } catch (error) {
      this.logger.error("Failed to get order from Salesforce", {
        orderId,
        error: extractErrorMessage(error),
      });
      throw error;
    }
  }

  // === GENERIC QUERY ===

  /**
   * Execute a SOQL query against Salesforce
   * This is a thin wrapper around the connection's query method for direct access
   */
  async query<T>(
    soql: string,
    options: { label?: string } = {}
  ): Promise<{ records: T[]; totalSize: number }> {
    if (!this.connection.isConnected()) {
      throw new SalesforceOperationException("Salesforce connection not available", {
        operation: "query",
        soql: soql.slice(0, 100),
      });
    }
    return this.connection.query(soql, options) as Promise<{ records: T[]; totalSize: number }>;
  }

  // === HEALTH CHECK ===

  healthCheck(): boolean {
    try {
      return this.connection.isConnected();
    } catch (error) {
      this.logger.error("Salesforce health check failed", {
        error: extractErrorMessage(error),
      });
      return false;
    }
  }

  /**
   * Get the Salesforce Organization ID (extracted from OAuth during authentication)
   * Used for Email-to-Case thread references
   */
  getOrganizationId(): string | null {
    return this.connection.getOrganizationId();
  }
}
