import { Injectable, NotFoundException, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { WhmcsConnectionFacade } from "../facades/whmcs.facade.js";
import { WhmcsCacheService } from "../cache/whmcs-cache.service.js";
import { transformWhmcsClientResponse } from "@customer-portal/domain/customer/providers";
import type { WhmcsClient } from "@customer-portal/domain/customer";

/**
 * Service for discovering and verifying WHMCS accounts by email.
 * Separated from WhmcsClientService to isolate "discovery" logic from "authenticated" logic.
 */
@Injectable()
export class WhmcsAccountDiscoveryService {
  constructor(
    private readonly connectionService: WhmcsConnectionFacade,
    private readonly cacheService: WhmcsCacheService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Find a client by email address.
   * This is a "discovery" operation used during signup/login flows.
   * It uses a specialized cache to map Email -> Client ID.
   */
  async findClientByEmail(email: string): Promise<WhmcsClient | null> {
    try {
      // 1. Try to find client ID by email from cache
      const cachedClientId = await this.cacheService.getClientIdByEmail(email);
      if (cachedClientId) {
        this.logger.debug(`Cache hit for email-to-id: ${email} -> ${cachedClientId}`);
        // If we have ID, fetch the full client data (which has its own cache)
        return this.getClientDetailsById(cachedClientId);
      }

      // 2. If no mapping, fetch from API
      // We use a try-catch here because the connection service might throw if not found
      // or if the API returns a specific error for "no results"
      const response = await this.connectionService.getClientDetailsByEmail(email);

      if (!response || !response.client) {
        // Not found is a valid state for discovery (return null)
        return null;
      }

      const client = transformWhmcsClientResponse(response);

      // 3. Cache both the data and the mapping
      await Promise.all([
        this.cacheService.setClientData(client.id, client),
        this.cacheService.setClientIdByEmail(email, client.id),
      ]);

      this.logger.log(`Discovered client by email: ${email}`);
      return client;
    } catch (error) {
      // Handle "Not Found" specifically
      if (
        error instanceof NotFoundException ||
        (error instanceof Error && error.message.includes("not found"))
      ) {
        return null;
      }

      // Log other errors but don't crash - return null to indicate lookup failed safely
      this.logger.warn(
        {
          email,
          error: extractErrorMessage(error),
        },
        "Failed to discover client by email"
      );
      return null;
    }
  }

  /**
   * Helper to get details by ID, reusing the cache logic from ClientService logic
   * We duplicate this small fetch to avoid circular dependency or tight coupling with WhmcsClientService
   */
  private async getClientDetailsById(clientId: number): Promise<WhmcsClient> {
    // Try cache first
    const cached = await this.cacheService.getClientData(clientId);
    if (cached) {
      return cached;
    }

    const response = await this.connectionService.getClientDetails(clientId);
    if (!response || !response.client) {
      throw new NotFoundException(`Client ${clientId} not found`);
    }

    const client = transformWhmcsClientResponse(response);
    await this.cacheService.setClientData(client.id, client);
    return client;
  }
}
