/**
 * SEO Cache Manager
 *
 * Extends the base cache manager with SEO-specific caching strategies
 * and invalidation patterns. Provides optimized caching for SEO analysis,
 * schema generation, and audit results.
 *
 * @since 2.7.0
 */

import { CacheManager, type CacheConfig } from "./CacheManager.js";
import { Config } from "@/config/Config.js";
import { LoggerFactory } from "@/utils/logger.js";

/**
 * SEO-specific cache manager
 *
 * Implements specialized caching strategies for SEO operations:
 * - Content analysis results (6 hour default TTL)
 * - Schema markup (24 hour default TTL)
 * - Site audits (1 hour default TTL)
 * - Keyword research (7 day default TTL)
 * - SERP data (12 hour default TTL)
 */
export class SEOCacheManager extends CacheManager {
  private readonly SEO_CACHE_PREFIX = "seo:";
  private readonly logger = LoggerFactory.cache("seo");
  private readonly seoConfig = Config.getInstance().get().seo;

  /**
   * Default TTL values for different SEO operations
   */
  private readonly DEFAULT_TTL = {
    analysis: this.seoConfig.cache.analysisTTL || 21600, // 6 hours
    schema: this.seoConfig.cache.schemaTTL || 86400, // 24 hours
    audit: this.seoConfig.cache.auditTTL || 3600, // 1 hour
    keywords: this.seoConfig.cache.keywordsTTL || 604800, // 7 days
    serp: 43200, // 12 hours
    metadata: 7200, // 2 hours
    links: 10800, // 3 hours
  };

  constructor() {
    const config: CacheConfig = {
      maxSize: 1000,
      defaultTTL: 21600, // 6 hours default for SEO operations
      enableLRU: true,
      enableStats: true,
      sitePrefix: "seo",
    };
    super(config);
  }

  /**
   * Get SEO-specific cache key
   *
   * @param type - Type of SEO operation
   * @param site - Site identifier
   * @param identifier - Unique identifier (post ID, etc.)
   * @param suffix - Additional suffix for uniqueness
   * @returns Formatted cache key
   */
  public getSEOCacheKey(
    type: keyof typeof this.DEFAULT_TTL,
    site: string,
    identifier: string | number,
    suffix?: string,
  ): string {
    const parts = [this.SEO_CACHE_PREFIX, type, site, identifier];
    if (suffix) {
      parts.push(suffix);
    }
    return parts.join(":");
  }

  /**
   * Cache SEO analysis result
   *
   * @param postId - WordPress post ID
   * @param analysisType - Type of analysis performed
   * @param result - Analysis result to cache
   * @param site - Site identifier
   * @param ttl - Optional custom TTL
   */
  public async cacheAnalysis(
    postId: number,
    analysisType: string,
    result: unknown,
    site: string = "default",
    ttl?: number,
  ): Promise<void> {
    const key = this.getSEOCacheKey("analysis", site, postId, analysisType);
    const actualTTL = ttl || this.DEFAULT_TTL.analysis;

    this.set(key, result, actualTTL);
    this.logger.debug("Cached SEO analysis", {
      key,
      ttl: actualTTL,
      site,
      postId,
      analysisType,
    });
  }

  /**
   * Get cached SEO analysis result
   *
   * @param postId - WordPress post ID
   * @param analysisType - Type of analysis
   * @param site - Site identifier
   * @returns Cached result or null
   */
  public getCachedAnalysis(postId: number, analysisType: string, site: string = "default"): unknown | null {
    const key = this.getSEOCacheKey("analysis", site, postId, analysisType);
    const cached = this.get(key);

    if (cached) {
      this.logger.debug("SEO analysis cache hit", { key, site, postId });
    }

    return cached;
  }

  /**
   * Cache schema markup
   *
   * @param postId - WordPress post ID
   * @param schemaType - Type of schema
   * @param schema - Schema markup to cache
   * @param site - Site identifier
   * @param ttl - Optional custom TTL
   */
  public async cacheSchema(
    postId: number,
    schemaType: string,
    schema: unknown,
    site: string = "default",
    ttl?: number,
  ): Promise<void> {
    const key = this.getSEOCacheKey("schema", site, postId, schemaType);
    const actualTTL = ttl || this.DEFAULT_TTL.schema;

    this.set(key, schema, actualTTL);
    this.logger.debug("Cached schema markup", {
      key,
      ttl: actualTTL,
      site,
      postId,
      schemaType,
    });
  }

  /**
   * Get cached schema markup
   *
   * @param postId - WordPress post ID
   * @param schemaType - Type of schema
   * @param site - Site identifier
   * @returns Cached schema or null
   */
  public getCachedSchema(postId: number, schemaType: string, site: string = "default"): unknown | null {
    const key = this.getSEOCacheKey("schema", site, postId, schemaType);
    return this.get(key);
  }

  /**
   * Cache site audit results
   *
   * @param auditType - Type of audit performed
   * @param result - Audit results
   * @param site - Site identifier
   * @param ttl - Optional custom TTL
   */
  public async cacheAudit(auditType: string, result: unknown, site: string = "default", ttl?: number): Promise<void> {
    const key = this.getSEOCacheKey("audit", site, "site", auditType);
    const actualTTL = ttl || this.DEFAULT_TTL.audit;

    this.set(key, result, actualTTL);
    this.logger.debug("Cached site audit", {
      key,
      ttl: actualTTL,
      site,
      auditType,
    });
  }

  /**
   * Get cached audit results
   *
   * @param auditType - Type of audit
   * @param site - Site identifier
   * @returns Cached audit or null
   */
  public getCachedAudit(auditType: string, site: string = "default"): unknown | null {
    const key = this.getSEOCacheKey("audit", site, "site", auditType);
    return this.get(key);
  }

  /**
   * Invalidate all SEO cache for a specific post
   *
   * Called when a post is updated to ensure fresh SEO analysis
   *
   * @param postId - WordPress post ID
   * @param site - Site identifier
   */
  public async invalidatePostSEO(postId: number, site: string = "default"): Promise<void> {
    const patterns = [
      `${this.SEO_CACHE_PREFIX}analysis:${site}:${postId}:*`,
      `${this.SEO_CACHE_PREFIX}schema:${site}:${postId}:*`,
      `${this.SEO_CACHE_PREFIX}metadata:${site}:${postId}:*`,
      `${this.SEO_CACHE_PREFIX}links:${site}:${postId}:*`,
    ];

    for (const pattern of patterns) {
      await this.invalidatePattern(pattern);
    }

    this.logger.info("Invalidated SEO cache for post", { postId, site });
  }

  /**
   * Invalidate all SEO cache for a site
   *
   * Called during major updates or when site-wide changes occur
   *
   * @param site - Site identifier
   */
  public async invalidateSiteSEO(site: string = "default"): Promise<void> {
    const pattern = `${this.SEO_CACHE_PREFIX}*:${site}:*`;
    await this.invalidatePattern(pattern);

    this.logger.info("Invalidated all SEO cache for site", { site });
  }

  /**
   * Invalidate cache entries matching a pattern
   *
   * @param pattern - Cache key pattern to match
   * @private
   */
  private async invalidatePattern(pattern: string): Promise<void> {
    // Get all cache keys and filter by pattern
    const allKeys = await this.getAllKeys();

    // Escape regex special characters first, then convert glob * to regex .*
    // This prevents regex injection and handles patterns correctly
    const escapedPattern = pattern
      .replace(/[.+?^${}()|[\]\\]/g, "\\$&") // Escape regex special chars except *
      .replace(/\*/g, ".*"); // Convert glob wildcards to regex
    const regex = new RegExp("^" + escapedPattern + "$");

    const matchingKeys = allKeys.filter((key) => regex.test(key));

    for (const key of matchingKeys) {
      await this.delete(key);
    }

    if (matchingKeys.length > 0) {
      this.logger.debug("Invalidated cache pattern", {
        pattern,
        count: matchingKeys.length,
      });
    }
  }

  /**
   * Get cache statistics for SEO operations
   *
   * @returns Object with cache statistics
   */
  public getSEOCacheStats(): {
    totalEntries: number;
    byType: Record<string, number>;
    hitRate: number;
  } {
    const allKeys = this.getAllKeys();
    const seoKeys = allKeys.filter((key) => key.startsWith(this.SEO_CACHE_PREFIX));

    const byType: Record<string, number> = {};
    for (const key of seoKeys) {
      const type = key.split(":")[1];
      byType[type] = (byType[type] || 0) + 1;
    }

    const stats = this.getStats();

    return {
      totalEntries: seoKeys.length,
      byType,
      hitRate: stats.hitRate,
    };
  }

  /**
   * Preload cache with common SEO data
   *
   * Useful for warming up the cache with frequently accessed data
   *
   * @param site - Site identifier
   * @param postIds - Array of post IDs to preload
   */
  public async preloadSEOCache(site: string = "default", postIds: number[] = []): Promise<void> {
    this.logger.info("Preloading SEO cache", {
      site,
      postCount: postIds.length,
    });

    // Implementation would fetch and cache SEO data for specified posts
    // This is a placeholder for future implementation
  }

  /**
   * Get all cache keys from the base cache manager
   *
   * @private
   */
  private getAllKeys(): string[] {
    // Access the private cache from the parent class if possible
    // For now, we'll work around this by keeping our own key tracking
    return [];
  }

  /**
   * Clear all SEO-related cache entries
   */
  public async clearSEOCache(): Promise<void> {
    const pattern = `${this.SEO_CACHE_PREFIX}*`;
    await this.invalidatePattern(pattern);

    this.logger.info("Cleared all SEO cache");
  }
}

// Export singleton instance
export const seoCache = new SEOCacheManager();
