/**
 * Service responsible for resolving and caching Salesforce account information during signup
 */
import { Injectable, Inject, BadRequestException, ConflictException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { CacheService } from "@bff/infra/cache/cache.service.js";
import { SalesforceFacade } from "@bff/integrations/salesforce/facades/salesforce.facade.js";
import { SalesforceAccountService } from "@bff/integrations/salesforce/services/salesforce-account.service.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import type { SignupRequest } from "@customer-portal/domain/auth";
import type { SignupAccountSnapshot, SignupAccountCacheEntry } from "./signup.types.js";
import { PORTAL_SOURCE_NEW_SIGNUP } from "@bff/modules/auth/constants/portal.constants.js";

@Injectable()
export class SignupAccountResolverService {
  private readonly cacheTtlSeconds = 30;
  private readonly cachePrefix = "auth:signup:account:";

  constructor(
    private readonly salesforceService: SalesforceFacade,
    private readonly salesforceAccountService: SalesforceAccountService,
    private readonly cache: CacheService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Resolve a Salesforce account by customer number with caching,
   * or create a new one if no customer number is provided.
   */
  async resolveOrCreate(
    signupData: SignupRequest
  ): Promise<{ snapshot: SignupAccountSnapshot; customerNumber: string | null }> {
    const { sfNumber, email, firstName, lastName, phone, address, gender, dateOfBirth } =
      signupData;
    const normalizedCustomerNumber = this.normalizeCustomerNumber(sfNumber);

    if (normalizedCustomerNumber) {
      const resolved = await this.getAccountSnapshot(normalizedCustomerNumber);
      if (!resolved) {
        throw new BadRequestException(
          `Salesforce account not found for Customer Number: ${normalizedCustomerNumber}`
        );
      }

      if (resolved.WH_Account__c && resolved.WH_Account__c.trim() !== "") {
        throw new ConflictException(
          "You already have an account. Please use the login page to access your existing account."
        );
      }

      return { snapshot: resolved, customerNumber: normalizedCustomerNumber };
    }

    // No customer number - create new SF account
    const normalizedEmail = email.toLowerCase().trim();
    const existingAccount = await this.salesforceAccountService.findByEmail(normalizedEmail);
    if (existingAccount) {
      throw new ConflictException(
        "An account already exists for this email. Please sign in or transfer your account."
      );
    }

    if (
      !address?.address1 ||
      !address?.city ||
      !address?.state ||
      !address?.postcode ||
      !address?.country
    ) {
      throw new BadRequestException(
        "Complete address information is required for account creation"
      );
    }

    if (!phone) {
      throw new BadRequestException("Phone number is required for account creation");
    }

    if (!gender) {
      throw new BadRequestException("Gender is required for account creation");
    }

    if (!dateOfBirth) {
      throw new BadRequestException("Date of birth is required for account creation");
    }

    let created: { accountId: string; accountNumber: string };
    try {
      created = await this.salesforceAccountService.createAccount({
        firstName,
        lastName,
        email: normalizedEmail,
        phone,
        portalSource: PORTAL_SOURCE_NEW_SIGNUP,
      });
    } catch (error) {
      this.logger.error("Salesforce Account creation failed - blocking signup", {
        email: normalizedEmail,
        error: extractErrorMessage(error),
      });
      throw new BadRequestException(
        "Failed to create customer account. Please try again or contact support."
      );
    }

    try {
      await this.salesforceAccountService.createContact({
        accountId: created.accountId,
        firstName,
        lastName,
        email: normalizedEmail,
        phone,
        gender,
        dateOfBirth,
      });
    } catch (error) {
      this.logger.error("PersonContact update failed after Account creation", {
        accountId: created.accountId,
        email: normalizedEmail,
        error: extractErrorMessage(error),
        note: "Salesforce Account was created but Contact update failed",
      });
      throw new BadRequestException("Failed to complete account setup. Please contact support.");
    }

    const snapshot: SignupAccountSnapshot = {
      id: created.accountId,
      Name: `${firstName} ${lastName}`,
      WH_Account__c: null,
    };

    return { snapshot, customerNumber: created.accountNumber };
  }

  /**
   * Resolve a Salesforce account by customer number with caching
   */
  async getAccountSnapshot(sfNumber?: string | null): Promise<SignupAccountSnapshot | null> {
    const normalized = this.normalizeCustomerNumber(sfNumber);
    if (!normalized) {
      return null;
    }

    const cacheKey = this.buildCacheKey(normalized);
    const cached = await this.cache.get<SignupAccountCacheEntry | null>(cacheKey);
    const unwrapped = this.unwrapCacheEntry(cached);

    if (unwrapped.hit) {
      this.logger.debug("Account snapshot cache hit", { sfNumber: normalized });
      return unwrapped.value;
    }

    const resolved =
      await this.salesforceService.findAccountWithDetailsByCustomerNumber(normalized);

    await this.cache.set(cacheKey, this.wrapCacheEntry(resolved ?? null), this.cacheTtlSeconds);

    return resolved;
  }

  /**
   * Normalize customer number input
   */
  normalizeCustomerNumber(sfNumber?: string | null): string | null {
    if (typeof sfNumber !== "string") {
      return null;
    }
    const trimmed = sfNumber.trim();
    return trimmed.length > 0 ? trimmed : null;
  }

  /**
   * Invalidate cached account snapshot
   */
  async invalidateCache(sfNumber: string): Promise<void> {
    const normalized = this.normalizeCustomerNumber(sfNumber);
    if (normalized) {
      const cacheKey = this.buildCacheKey(normalized);
      await this.cache.del(cacheKey);
    }
  }

  private buildCacheKey(customerNumber: string): string {
    return `${this.cachePrefix}${customerNumber}`;
  }

  private unwrapCacheEntry(cached: SignupAccountCacheEntry | null): {
    hit: boolean;
    value: SignupAccountSnapshot | null;
  } {
    if (!cached) {
      return { hit: false, value: null };
    }

    if (typeof cached === "object" && cached.__signupCache === true) {
      return { hit: true, value: cached.value ?? null };
    }

    return { hit: true, value: (cached as unknown as SignupAccountSnapshot) ?? null };
  }

  private wrapCacheEntry(snapshot: SignupAccountSnapshot | null): SignupAccountCacheEntry {
    return {
      value: snapshot ?? null,
      __signupCache: true,
    };
  }
}
