import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { CacheService } from "@bff/infra/cache/cache.service";
import type { UserIdMapping } from "@customer-portal/domain/mappings";
import { getErrorMessage } from "@bff/core/utils/error.util";

@Injectable()
export class MappingCacheService {
  private readonly CACHE_TTL = 1800;
  private readonly CACHE_PREFIX = "mapping";

  constructor(
    private readonly cacheService: CacheService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  async getByUserId(userId: string): Promise<UserIdMapping | null> {
    const key = this.buildCacheKey("userId", userId);
    return this.get(key);
  }

  async getByWhmcsClientId(whmcsClientId: number): Promise<UserIdMapping | null> {
    const key = this.buildCacheKey("whmcsClientId", whmcsClientId);
    return this.get(key);
  }

  async getBySfAccountId(sfAccountId: string): Promise<UserIdMapping | null> {
    const key = this.buildCacheKey("sfAccountId", sfAccountId);
    return this.get(key);
  }

  async setMapping(mapping: UserIdMapping): Promise<void> {
    const operations: Promise<void>[] = [];
    const userKey = this.buildCacheKey("userId", mapping.userId);
    operations.push(this.set(userKey, mapping));
    if (mapping.whmcsClientId) {
      const whmcsKey = this.buildCacheKey("whmcsClientId", mapping.whmcsClientId);
      operations.push(this.set(whmcsKey, mapping));
    }
    if (mapping.sfAccountId) {
      const sfKey = this.buildCacheKey("sfAccountId", mapping.sfAccountId);
      operations.push(this.set(sfKey, mapping));
    }
    await Promise.all(operations);
    this.logger.debug(`Cached mapping for user ${mapping.userId}`, {
      whmcsClientId: mapping.whmcsClientId,
      sfAccountId: mapping.sfAccountId,
    });
  }

  async deleteMapping(mapping: UserIdMapping): Promise<void> {
    const keys: string[] = [];
    keys.push(this.buildCacheKey("userId", mapping.userId));
    if (mapping.whmcsClientId) {
      keys.push(this.buildCacheKey("whmcsClientId", mapping.whmcsClientId));
    }
    if (mapping.sfAccountId) {
      keys.push(this.buildCacheKey("sfAccountId", mapping.sfAccountId));
    }
    await Promise.all(keys.map(key => this.cacheService.del(key)));
    this.logger.debug(`Deleted mapping cache for user ${mapping.userId}`);
  }

  async updateMapping(oldMapping: UserIdMapping, newMapping: UserIdMapping): Promise<void> {
    await this.deleteMapping(oldMapping);
    await this.setMapping(newMapping);
    this.logger.debug(`Updated mapping cache for user ${newMapping.userId}`, {
      oldWhmcsClientId: oldMapping.whmcsClientId,
      newWhmcsClientId: newMapping.whmcsClientId,
      oldSfAccountId: oldMapping.sfAccountId,
      newSfAccountId: newMapping.sfAccountId,
    });
  }

  async clearAll(): Promise<void> {
    try {
      await this.cacheService.delPattern(`${this.CACHE_PREFIX}:*`);
      this.logger.log("Cleared all mapping cache");
    } catch (error) {
      this.logger.error("Failed to clear mapping cache", {
        error: getErrorMessage(error),
      });
    }
  }

  async getStats(): Promise<{ totalKeys: number; memoryUsage: number }> {
    const pattern = `${this.CACHE_PREFIX}:*`;
    try {
      const totalKeys = await this.cacheService.countByPattern(pattern);
      const memoryUsage =
        totalKeys === 0 ? 0 : await this.cacheService.memoryUsageByPattern(pattern);
      return { totalKeys, memoryUsage };
    } catch (error) {
      this.logger.error("Failed to get cache stats", { error: getErrorMessage(error) });
      return { totalKeys: 0, memoryUsage: 0 };
    }
  }

  private async get<T>(key: string): Promise<T | null> {
    try {
      return await this.cacheService.get<T>(key);
    } catch (error) {
      this.logger.error(`Failed to get cache key: ${key}`, { error: getErrorMessage(error) });
      return null;
    }
  }

  private async set(key: string, value: unknown, ttlSeconds?: number): Promise<void> {
    try {
      await this.cacheService.set(key, value, ttlSeconds || this.CACHE_TTL);
    } catch (error) {
      this.logger.error(`Failed to set cache key: ${key}`, { error: getErrorMessage(error) });
    }
  }

  private buildCacheKey(type: string, value: string | number): string {
    return `${this.CACHE_PREFIX}:${type}:${value}`;
  }
}
