import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { getErrorMessage } from "@bff/core/utils/error.util";
import { SalesforceConnection } from "./salesforce-connection.service";
import type { SalesforceAccountRecord } from "@customer-portal/domain/customer";
import type { SalesforceResponse } from "@customer-portal/domain/common";
import { customerNumberSchema, salesforceIdSchema } from "@customer-portal/domain/common";

/**
 * Salesforce Account Service
 *
 * Only contains methods that are actually used in the codebase:
 * - findByCustomerNumber() - Used in signup/WHMCS linking workflows
 * - getAccountDetails() - Used in signup to check WH_Account__c field
 */
@Injectable()
export class SalesforceAccountService {
  constructor(
    private connection: SalesforceConnection,
    private readonly configService: ConfigService,
    @Inject(Logger) private readonly logger: Logger
  ) {
    this.portalStatusField =
      this.configService.get<string>("ACCOUNT_PORTAL_STATUS_FIELD") ?? "Portal_Status__c";
    this.portalSourceField =
      this.configService.get<string>("ACCOUNT_PORTAL_STATUS_SOURCE_FIELD") ??
      "Portal_Registration_Source__c";
    this.portalLastSignedInField =
      this.configService.get<string>("ACCOUNT_PORTAL_LAST_SIGNED_IN_FIELD") ??
      "Portal_Last_SignIn__c";
  }

  private readonly portalStatusField: string;
  private readonly portalSourceField: string;
  private readonly portalLastSignedInField: string;

  /**
   * Find Salesforce account by customer number (SF_Account_No__c field)
   * Used during signup and WHMCS linking to verify customer exists
   */
  async findByCustomerNumber(customerNumber: string): Promise<{ id: string } | null> {
    const validCustomerNumber = customerNumberSchema.parse(customerNumber);

    try {
      const result = (await this.connection.query(
        `SELECT Id FROM Account WHERE SF_Account_No__c = '${this.safeSoql(validCustomerNumber)}'`,
        { label: "auth:findAccountByCustomerNumber" }
      )) as SalesforceResponse<SalesforceAccountRecord>;
      return result.totalSize > 0 ? { id: result.records[0]?.Id ?? "" } : null;
    } catch (error) {
      this.logger.error("Failed to find account by customer number", {
        error: getErrorMessage(error),
      });
      throw new Error("Failed to find account");
    }
  }

  async findWithDetailsByCustomerNumber(customerNumber: string): Promise<{
    id: string;
    Name?: string | null;
    WH_Account__c?: string | null;
  } | null> {
    const validCustomerNumber = customerNumberSchema.parse(customerNumber);

    try {
      const result = (await this.connection.query(
        `SELECT Id, Name, WH_Account__c FROM Account WHERE SF_Account_No__c = '${this.safeSoql(validCustomerNumber)}'`,
        { label: "auth:findAccountWithDetails" }
      )) as SalesforceResponse<SalesforceAccountRecord>;

      if (result.totalSize === 0) {
        return null;
      }

      const record = result.records[0];
      return {
        id: record?.Id ?? "",
        Name: record?.Name,
        WH_Account__c: record?.WH_Account__c || undefined,
      };
    } catch (error) {
      this.logger.error("Failed to find account with details by customer number", {
        error: getErrorMessage(error),
      });
      throw new Error("Failed to find account");
    }
  }

  /**
   * Get account details including WH_Account__c field
   * Used in signup workflow to check if account is already linked to WHMCS
   */
  async getAccountDetails(
    accountId: string
  ): Promise<{ id: string; WH_Account__c?: string | null; Name?: string | null } | null> {
    const validAccountId = salesforceIdSchema.parse(accountId);

    try {
      const result = (await this.connection.query(
        `SELECT Id, Name, WH_Account__c FROM Account WHERE Id = '${this.safeSoql(validAccountId)}'`,
        { label: "auth:getAccountDetails" }
      )) as SalesforceResponse<SalesforceAccountRecord>;

      if (result.totalSize === 0) {
        return null;
      }

      const record = result.records[0];
      return {
        id: record?.Id ?? "",
        Name: record?.Name,
        WH_Account__c: record?.WH_Account__c || undefined,
      };
    } catch (error) {
      this.logger.error("Failed to get account details", {
        accountId,
        error: getErrorMessage(error),
      });
      throw new Error("Failed to get account details");
    }
  }

  /**
   * Escape single quotes for SOQL queries
   * @private
   */
  private safeSoql(input: string): string {
    return input.replace(/'/g, "\\'");
  }

  async updatePortalFields(
    accountId: string,
    update: SalesforceAccountPortalUpdate
  ): Promise<void> {
    const validAccountId = salesforceIdSchema.parse(accountId);
    const payload: Record<string, unknown> = { Id: validAccountId };

    if (update.status) {
      payload[this.portalStatusField] = update.status;
    }

    if (update.source) {
      payload[this.portalSourceField] = update.source;
    }

    if (update.lastSignedInAt) {
      payload[this.portalLastSignedInField] = update.lastSignedInAt.toISOString();
    }

    if (Object.keys(payload).length <= 1) {
      return;
    }

    try {
      const updateMethod = this.connection.sobject("Account").update;
      if (!updateMethod) {
        this.logger.warn("Salesforce update method not available");
        return;
      }

      await updateMethod(payload as Record<string, unknown> & { Id: string });
      this.logger.debug("Updated Salesforce account portal fields", {
        accountId: validAccountId,
        hasStatus: Boolean(update.status),
        hasSource: Boolean(update.source),
        hasLastSignedIn: Boolean(update.lastSignedInAt),
      });
    } catch (error) {
      this.logger.warn("Failed to update account portal fields", {
        accountId: validAccountId,
        error: getErrorMessage(error),
      });
    }
  }
}

export interface SalesforceAccountPortalUpdate {
  status?: string;
  source?: string;
  lastSignedInAt?: Date;
}
