/**
 * TenantAwareCache - 테넌트별 데이터 격리 캐시 유틸리티
 *
 * 기능:
 * - tenant.id 기반 캐시 키 생성 (예: 'mes-282-itemMasters')
 * - TTL (Time To Live) 만료 처리
 * - tenant.id 자동 검증
 * - 손상된 캐시 자동 제거
 * - localStorage 및 sessionStorage 지원
 */

interface CachedData<T> {
  tenantId: number;              // 테넌트 ID (number)
  data: T;                       // 실제 데이터
  timestamp: number;             // 저장 시간 (ms)
  version?: string;              // 버전 정보 (선택)
}

export class TenantAwareCache {
  private tenantId: number;      // 테넌트 ID
  private storage: Storage;      // localStorage | sessionStorage
  private ttl: number;           // Time to Live (ms)

  /**
   * TenantAwareCache 생성자
   *
   * @param tenantId - 테넌트 ID (user.tenant.id)
   * @param storage - 사용할 스토리지 (기본: sessionStorage)
   * @param ttl - 캐시 만료 시간 (기본: 1시간)
   *
   * @example
   * const cache = new TenantAwareCache(282, sessionStorage, 3600000);
   * cache.set('itemMasters', data);
   */
  constructor(
    tenantId: number,
    storage: Storage = sessionStorage,
    ttl: number = 3600000  // 1시간 기본값
  ) {
    this.tenantId = tenantId;
    this.storage = storage;
    this.ttl = ttl;
  }

  /**
   * 테넌트별 고유 키 생성
   *
   * @param key - 기본 키 이름
   * @returns tenant.id가 포함된 고유 키
   *
   * @example
   * getKey('itemMasters') → 'mes-282-itemMasters'
   */
  private getKey(key: string): string {
    return `mes-${this.tenantId}-${key}`;
  }

  /**
   * 캐시에 데이터 저장
   *
   * @param key - 캐시 키
   * @param data - 저장할 데이터
   * @param version - 버전 정보 (선택)
   *
   * @example
   * cache.set('itemMasters', [item1, item2], '1.0');
   */
  set<T>(key: string, data: T, version?: string): void {
    const cacheData: CachedData<T> = {
      tenantId: this.tenantId,
      data,
      timestamp: Date.now(),
      version
    };

    this.storage.setItem(this.getKey(key), JSON.stringify(cacheData));
  }

  /**
   * 캐시에서 데이터 조회 (tenantId 및 TTL 검증 포함)
   *
   * @param key - 캐시 키
   * @returns 캐시된 데이터 또는 null
   *
   * @example
   * const data = cache.get<ItemMaster[]>('itemMasters');
   * if (data) {
   *   * } else {
   *   * }
   */
  get<T>(key: string): T | null {
    const cached = this.storage.getItem(this.getKey(key));
    if (!cached) return null;

    try {
      const parsed: CachedData<T> = JSON.parse(cached);

      // 🛡️ 1. tenantId 검증
      if (parsed.tenantId !== this.tenantId) {
        console.warn(
          `[Cache] tenantId mismatch for key "${key}": ` +
          `${parsed.tenantId} !== ${this.tenantId}`
        );
        this.remove(key);
        return null;
      }

      // 🛡️ 2. TTL 검증 (만료 시간)
      if (Date.now() - parsed.timestamp > this.ttl) {
        console.warn(`[Cache] Expired cache for key: ${key}`);
        this.remove(key);
        return null;
      }

      return parsed.data;
    } catch (error) {
      console.error(`[Cache] Parse error for key: ${key}`, error);
      this.remove(key);
      return null;
    }
  }

  /**
   * 캐시에서 특정 키 삭제
   *
   * @param key - 삭제할 캐시 키
   *
   * @example
   * cache.remove('itemMasters');
   */
  remove(key: string): void {
    this.storage.removeItem(this.getKey(key));
  }

  /**
   * 현재 테넌트의 모든 캐시 삭제
   *
   * @example
   * cache.clear(); // 'mes-282-*' 모두 삭제
   */
  clear(): void {
    const prefix = `mes-${this.tenantId}-`;

    Object.keys(this.storage).forEach(key => {
      if (key.startsWith(prefix)) {
        this.storage.removeItem(key);
      }
    });
  }

  /**
   * 버전 일치 여부 확인
   *
   * @param key - 캐시 키
   * @param expectedVersion - 기대하는 버전
   * @returns 버전 일치 여부
   *
   * @example
   * if (!cache.isVersionMatch('itemMasters', '1.0')) {
   *   // 버전 불일치 - 재조회 필요
   *   const newData = await fetchFromAPI();
   *   cache.set('itemMasters', newData, '1.0');
   * }
   */
  isVersionMatch(key: string, expectedVersion: string): boolean {
    const cached = this.storage.getItem(this.getKey(key));
    if (!cached) return false;

    try {
      const parsed: CachedData<any> = JSON.parse(cached);
      return parsed.version === expectedVersion;
    } catch {
      return false;
    }
  }

  /**
   * 캐시 메타데이터 조회
   *
   * @param key - 캐시 키
   * @returns 메타데이터 또는 null
   *
   * @example
   * const meta = cache.getMetadata('itemMasters');
   * if (meta) {
   *   *   * }
   */
  getMetadata(key: string): { tenantId: number; timestamp: number; version?: string } | null {
    const cached = this.storage.getItem(this.getKey(key));
    if (!cached) return null;

    try {
      const parsed: CachedData<any> = JSON.parse(cached);
      return {
        tenantId: parsed.tenantId,
        timestamp: parsed.timestamp,
        version: parsed.version
      };
    } catch {
      return null;
    }
  }

  /**
   * 캐시 존재 여부 확인
   *
   * @param key - 캐시 키
   * @returns 캐시 존재 여부
   *
   * @example
   * if (cache.has('itemMasters')) {
   *   const data = cache.get('itemMasters');
   * }
   */
  has(key: string): boolean {
    return this.storage.getItem(this.getKey(key)) !== null;
  }

  /**
   * 현재 테넌트 ID 반환
   *
   * @returns 테넌트 ID
   *
   * @example
   * console.log('현재 테넌트:', cache.getTenantId()); // 282
   */
  getTenantId(): number {
    return this.tenantId;
  }

  /**
   * 캐시 통계 정보 조회
   *
   * @returns 캐시 통계
   *
   * @example
   * const stats = cache.getStats();
   *   */
  getStats(): { count: number; totalSize: number; keys: string[] } {
    const prefix = `mes-${this.tenantId}-`;
    const keys: string[] = [];
    let totalSize = 0;

    Object.keys(this.storage).forEach(key => {
      if (key.startsWith(prefix)) {
        keys.push(key);
        const value = this.storage.getItem(key);
        if (value) {
          totalSize += value.length;
        }
      }
    });

    return {
      count: keys.length,
      totalSize,
      keys
    };
  }
}