import {
  CacheEntry,
  CacheLevel,
  CacheProvider,
  ContentType,
  CacheStatus,
} from "./types";

export abstract class BaseCacheProvider {
  protected level: CacheLevel;
  protected provider: CacheProvider;

  constructor(level: CacheLevel, provider: CacheProvider) {
    this.level = level;
    this.provider = provider;
  }

  abstract get<T>(key: string): Promise<CacheEntry<T> | null>;
  abstract set<T>(entry: CacheEntry<T>): Promise<void>;
  abstract delete(key: string): Promise<boolean>;
  abstract clear(): Promise<void>;
  abstract keys(pattern?: string): Promise<string[]>;
  abstract exists(key: string): Promise<boolean>;
  abstract size(): Promise<number>;
  abstract healthCheck(): Promise<boolean>;
}

/**
 * 브라우저 캐시 프로바이더 (localStorage/sessionStorage)
 */
export class BrowserCacheProvider extends BaseCacheProvider {
  private storage: Storage;

  constructor(useSessionStorage = false) {
    super("browser", "browser");
    this.storage = useSessionStorage ? sessionStorage : localStorage;
  }

  async get<T>(key: string): Promise<CacheEntry<T> | null> {
    try {
      const item = this.storage.getItem(`cache:${key}`);
      if (!item) return null;

      const entry = JSON.parse(item);
      entry.createdAt = new Date(entry.createdAt);
      entry.expiresAt = new Date(entry.expiresAt);
      entry.lastAccessed = new Date(entry.lastAccessed);

      // 만료 확인
      if (entry.expiresAt < new Date()) {
        await this.delete(key);
        return null;
      }

      // 액세스 업데이트
      entry.hits++;
      entry.lastAccessed = new Date();
      this.storage.setItem(`cache:${key}`, JSON.stringify(entry));

      return entry;
    } catch (error) {
      console.error("브라우저 캐시 조회 오류:", error);
      return null;
    }
  }

  async set<T>(entry: CacheEntry<T>): Promise<void> {
    try {
      this.storage.setItem(`cache:${entry.key}`, JSON.stringify(entry));
    } catch (error) {
      // 저장 공간 부족 시 오래된 항목 정리
      if (error.name === "QuotaExceededError") {
        await this.evictOldEntries();
        this.storage.setItem(`cache:${entry.key}`, JSON.stringify(entry));
      } else {
        throw error;
      }
    }
  }

  async delete(key: string): Promise<boolean> {
    this.storage.removeItem(`cache:${key}`);
    return true;
  }

  async clear(): Promise<void> {
    const keys = await this.keys();
    keys.forEach((key) => this.storage.removeItem(key));
  }

  async keys(pattern?: string): Promise<string[]> {
    const keys: string[] = [];
    for (let i = 0; i < this.storage.length; i++) {
      const key = this.storage.key(i);
      if (key?.startsWith("cache:")) {
        const cacheKey = key.replace("cache:", "");
        if (!pattern || cacheKey.includes(pattern)) {
          keys.push(key);
        }
      }
    }
    return keys;
  }

  async exists(key: string): Promise<boolean> {
    return this.storage.getItem(`cache:${key}`) !== null;
  }

  async size(): Promise<number> {
    return (await this.keys()).length;
  }

  async healthCheck(): Promise<boolean> {
    try {
      const testKey = "health-check";
      this.storage.setItem(testKey, "test");
      this.storage.removeItem(testKey);
      return true;
    } catch {
      return false;
    }
  }

  private async evictOldEntries(): Promise<void> {
    const keys = await this.keys();
    const entries: Array<{ key: string; lastAccessed: Date }> = [];

    // 모든 엔트리의 최종 액세스 시간 수집
    for (const key of keys) {
      try {
        const item = this.storage.getItem(key);
        if (item) {
          const entry = JSON.parse(item);
          entries.push({
            key,
            lastAccessed: new Date(entry.lastAccessed),
          });
        }
      } catch {
        // 파싱 오류 시 해당 키 삭제
        this.storage.removeItem(key);
      }
    }

    // 오래된 순으로 정렬하여 25% 제거
    entries.sort((a, b) => a.lastAccessed.getTime() - b.lastAccessed.getTime());
    const toEvict = Math.ceil(entries.length * 0.25);

    for (let i = 0; i < toEvict; i++) {
      this.storage.removeItem(entries[i].key);
    }
  }
}

/**
 * 메모리 캐시 프로바이더 (인메모리)
 */
export class MemoryCacheProvider extends BaseCacheProvider {
  private cache = new Map<string, CacheEntry<any>>();
  private maxSize: number;

  constructor(maxSize = 1000) {
    super("memory", "memory");
    this.maxSize = maxSize;
    this.startCleanupTimer();
  }

  async get<T>(key: string): Promise<CacheEntry<T> | null> {
    const entry = this.cache.get(key);
    if (!entry) return null;

    // 만료 확인
    if (entry.expiresAt < new Date()) {
      this.cache.delete(key);
      return null;
    }

    // 액세스 업데이트
    entry.hits++;
    entry.lastAccessed = new Date();
    return entry;
  }

  async set<T>(entry: CacheEntry<T>): Promise<void> {
    // 크기 제한 확인
    if (this.cache.size >= this.maxSize) {
      await this.evictLRU();
    }

    this.cache.set(entry.key, entry);
  }

  async delete(key: string): Promise<boolean> {
    return this.cache.delete(key);
  }

  async clear(): Promise<void> {
    this.cache.clear();
  }

  async keys(pattern?: string): Promise<string[]> {
    const keys = Array.from(this.cache.keys());
    return pattern ? keys.filter((key) => key.includes(pattern)) : keys;
  }

  async exists(key: string): Promise<boolean> {
    return this.cache.has(key);
  }

  async size(): Promise<number> {
    return this.cache.size;
  }

  async healthCheck(): Promise<boolean> {
    return true; // 메모리 캐시는 항상 건강함
  }

  private async evictLRU(): Promise<void> {
    // LRU 방식으로 오래된 항목 제거
    let oldestKey = "";
    let oldestTime = Date.now();

    for (const [key, entry] of this.cache.entries()) {
      if (entry.lastAccessed.getTime() < oldestTime) {
        oldestTime = entry.lastAccessed.getTime();
        oldestKey = key;
      }
    }

    if (oldestKey) {
      this.cache.delete(oldestKey);
    }
  }

  private startCleanupTimer(): void {
    // 5분마다 만료된 항목 정리
    setInterval(() => {
      const now = new Date();
      for (const [key, entry] of this.cache.entries()) {
        if (entry.expiresAt < now) {
          this.cache.delete(key);
        }
      }
    }, 5 * 60 * 1000);
  }
}

/**
 * Redis 캐시 프로바이더 (서버 사이드)
 */
export class RedisCacheProvider extends BaseCacheProvider {
  private client: any; // Redis 클라이언트
  private connected = false;

  constructor(redisClient?: any) {
    super("server", "redis");
    this.client = redisClient;
  }

  async connect(config?: {
    host: string;
    port: number;
    password?: string;
    db?: number;
  }): Promise<void> {
    if (!this.client && config) {
      // Redis 클라이언트 생성 (실제 구현에서는 redis 라이브러리 사용)
      console.log("Redis 연결 설정:", config);
      this.connected = true;
    }
  }

  async get<T>(key: string): Promise<CacheEntry<T> | null> {
    if (!this.connected) return null;

    try {
      const data = await this.client?.get(`cache:${key}`);
      if (!data) return null;

      const entry = JSON.parse(data);
      entry.createdAt = new Date(entry.createdAt);
      entry.expiresAt = new Date(entry.expiresAt);
      entry.lastAccessed = new Date(entry.lastAccessed);

      // 만료 확인
      if (entry.expiresAt < new Date()) {
        await this.delete(key);
        return null;
      }

      // 액세스 업데이트
      entry.hits++;
      entry.lastAccessed = new Date();
      await this.client?.set(`cache:${key}`, JSON.stringify(entry));

      return entry;
    } catch (error) {
      console.error("Redis 캐시 조회 오류:", error);
      return null;
    }
  }

  async set<T>(entry: CacheEntry<T>): Promise<void> {
    if (!this.connected) return;

    try {
      const ttl = Math.ceil((entry.expiresAt.getTime() - Date.now()) / 1000);
      await this.client?.setex(
        `cache:${entry.key}`,
        ttl,
        JSON.stringify(entry)
      );
    } catch (error) {
      console.error("Redis 캐시 저장 오류:", error);
      throw error;
    }
  }

  async delete(key: string): Promise<boolean> {
    if (!this.connected) return false;

    try {
      const result = await this.client?.del(`cache:${key}`);
      return result > 0;
    } catch (error) {
      console.error("Redis 캐시 삭제 오류:", error);
      return false;
    }
  }

  async clear(): Promise<void> {
    if (!this.connected) return;

    try {
      const keys = await this.keys();
      if (keys.length > 0) {
        await this.client?.del(...keys);
      }
    } catch (error) {
      console.error("Redis 캐시 클리어 오류:", error);
    }
  }

  async keys(pattern = "*"): Promise<string[]> {
    if (!this.connected) return [];

    try {
      return (await this.client?.keys(`cache:${pattern}`)) || [];
    } catch (error) {
      console.error("Redis 키 조회 오류:", error);
      return [];
    }
  }

  async exists(key: string): Promise<boolean> {
    if (!this.connected) return false;

    try {
      const result = await this.client?.exists(`cache:${key}`);
      return result > 0;
    } catch (error) {
      console.error("Redis 존재 확인 오류:", error);
      return false;
    }
  }

  async size(): Promise<number> {
    if (!this.connected) return 0;

    try {
      const keys = await this.keys();
      return keys.length;
    } catch (error) {
      console.error("Redis 크기 조회 오류:", error);
      return 0;
    }
  }

  async healthCheck(): Promise<boolean> {
    if (!this.connected) return false;

    try {
      await this.client?.ping();
      return true;
    } catch {
      return false;
    }
  }
}

/**
 * CDN 캐시 프로바이더 (Cloudflare API)
 */
export class CDNCacheProvider extends BaseCacheProvider {
  private apiToken: string;
  private zoneId: string;

  constructor(apiToken: string, zoneId: string) {
    super("cdn", "cloudflare");
    this.apiToken = apiToken;
    this.zoneId = zoneId;
  }

  async get<T>(_key: string): Promise<CacheEntry<T> | null> {
    // CDN 캐시는 직접 조회할 수 없음
    return null;
  }

  async set<T>(_entry: CacheEntry<T>): Promise<void> {
    // CDN 캐시는 요청에 따라 자동으로 설정됨
  }

  async delete(key: string): Promise<boolean> {
    try {
      // Cloudflare API를 사용하여 캐시 무효화
      const response = await fetch(
        `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/purge_cache`,
        {
          method: "POST",
          headers: {
            Authorization: `Bearer ${this.apiToken}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            files: [key],
          }),
        }
      );

      return response.ok;
    } catch (error) {
      console.error("CDN 캐시 무효화 오류:", error);
      return false;
    }
  }

  async clear(): Promise<void> {
    try {
      // 전체 캐시 무효화
      await fetch(
        `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/purge_cache`,
        {
          method: "POST",
          headers: {
            Authorization: `Bearer ${this.apiToken}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            purge_everything: true,
          }),
        }
      );
    } catch (error) {
      console.error("CDN 전체 캐시 클리어 오류:", error);
    }
  }

  async keys(_pattern?: string): Promise<string[]> {
    // CDN에서는 키 목록을 조회할 수 없음
    return [];
  }

  async exists(_key: string): Promise<boolean> {
    // CDN에서는 존재 여부를 직접 확인할 수 없음
    return false;
  }

  async size(): Promise<number> {
    // CDN에서는 크기를 직접 측정할 수 없음
    return 0;
  }

  async healthCheck(): Promise<boolean> {
    try {
      // Cloudflare API 상태 확인
      const response = await fetch(
        `https://api.cloudflare.com/client/v4/zones/${this.zoneId}`,
        {
          headers: {
            Authorization: `Bearer ${this.apiToken}`,
          },
        }
      );

      return response.ok;
    } catch {
      return false;
    }
  }
}

/**
 * 프로바이더 팩토리
 */
export class CacheProviderFactory {
  static createProvider(
    level: CacheLevel,
    provider: CacheProvider,
    options?: any
  ): BaseCacheProvider {
    switch (provider) {
      case "browser":
        return new BrowserCacheProvider(options?.useSessionStorage);

      case "memory":
        return new MemoryCacheProvider(options?.maxSize);

      case "redis":
        return new RedisCacheProvider(options?.client);

      case "cloudflare":
        return new CDNCacheProvider(options?.apiToken, options?.zoneId);

      default:
        throw new Error(`지원하지 않는 캐시 프로바이더: ${provider}`);
    }
  }
}

export {
  BaseCacheProvider,
  BrowserCacheProvider,
  MemoryCacheProvider,
  RedisCacheProvider,
  CDNCacheProvider,
};
