/**
 * Longevity Tracker
 *
 * Tracks how often entities appear in reranking results.
 * Entities that persistently appear are likely more important.
 */

/**
 * Record for tracking entity appearances
 */
interface LongevityRecord {
  /** Number of times entity has appeared */
  appearanceCount: number;
  /** First appearance timestamp */
  firstSeen: Date;
  /** Last appearance timestamp */
  lastSeen: Date;
}

/**
 * Maximum entries to keep in the tracker (LRU eviction)
 */
const MAX_ENTRIES = 1000;

/**
 * Longevity Tracker
 *
 * In-memory tracker that records when entities appear in reranking results.
 * Uses LRU eviction when max entries is reached.
 */
export class LongevityTracker {
  private records: Map<string, LongevityRecord> = new Map();
  private accessOrder: string[] = [];

  /**
   * Record an entity appearance
   */
  recordAppearance(qualifiedName: string): void {
    const now = new Date();
    const existing = this.records.get(qualifiedName);

    if (existing) {
      existing.appearanceCount++;
      existing.lastSeen = now;
      // Move to end of access order
      this.accessOrder = this.accessOrder.filter((n) => n !== qualifiedName);
      this.accessOrder.push(qualifiedName);
    } else {
      // Evict oldest if at capacity
      if (this.records.size >= MAX_ENTRIES) {
        const oldest = this.accessOrder.shift();
        if (oldest) {
          this.records.delete(oldest);
        }
      }

      this.records.set(qualifiedName, {
        appearanceCount: 1,
        firstSeen: now,
        lastSeen: now,
      });
      this.accessOrder.push(qualifiedName);
    }
  }

  /**
   * Record multiple entity appearances
   */
  recordAppearances(qualifiedNames: string[]): void {
    for (const name of qualifiedNames) {
      this.recordAppearance(name);
    }
  }

  /**
   * Get longevity score for an entity
   *
   * Score formula: log(appearance_count) / 3
   * - 1 appearance = 0.0 (first time)
   * - 2 appearances = 0.23
   * - 5 appearances = 0.54
   * - 10 appearances = 0.77
   * - 20+ appearances = 1.0 (capped)
   */
  getLongevityScore(qualifiedName: string): number {
    const record = this.records.get(qualifiedName);
    if (!record || record.appearanceCount <= 1) {
      return 0;
    }

    const score = Math.log(record.appearanceCount) / 3;
    return Math.min(score, 1.0);
  }

  /**
   * Get appearance count for an entity
   */
  getAppearanceCount(qualifiedName: string): number {
    return this.records.get(qualifiedName)?.appearanceCount ?? 0;
  }

  /**
   * Get all tracked entities
   */
  getTrackedEntities(): string[] {
    return Array.from(this.records.keys());
  }

  /**
   * Get tracker size
   */
  get size(): number {
    return this.records.size;
  }

  /**
   * Clear all records
   */
  clear(): void {
    this.records.clear();
    this.accessOrder = [];
  }

  /**
   * Export records for persistence
   */
  export(): Record<string, LongevityRecord> {
    const result: Record<string, LongevityRecord> = {};
    for (const [key, value] of this.records) {
      result[key] = { ...value };
    }
    return result;
  }

  /**
   * Import records from persistence
   */
  import(records: Record<string, LongevityRecord>): void {
    this.clear();
    for (const [key, value] of Object.entries(records)) {
      this.records.set(key, {
        appearanceCount: value.appearanceCount,
        firstSeen: new Date(value.firstSeen),
        lastSeen: new Date(value.lastSeen),
      });
      this.accessOrder.push(key);
    }
  }
}

/**
 * Global longevity tracker instance
 */
let globalTracker: LongevityTracker | null = null;

/**
 * Get the global longevity tracker
 */
export function getLongevityTracker(): LongevityTracker {
  if (!globalTracker) {
    globalTracker = new LongevityTracker();
  }
  return globalTracker;
}

/**
 * Record reranked items in the global tracker
 */
export function recordRerankedItems(qualifiedNames: string[]): void {
  getLongevityTracker().recordAppearances(qualifiedNames);
}

/**
 * Get longevity score from the global tracker
 */
export function getLongevityScore(qualifiedName: string): number {
  return getLongevityTracker().getLongevityScore(qualifiedName);
}
