/**
 * Infrastructure: InMemoryPageViewRepository
 * 
 * In-memory implementation of IPageViewRepository for development/testing.
 */

import type { IPageViewRepository } from '@core/analytics/application/repositories/IPageViewRepository';
import { PageView, type EntityType } from '@core/analytics/domain/entities/PageView';
import { Logger } from '@core/shared/application';

export class InMemoryPageViewRepository implements IPageViewRepository {
  private pageViews: Map<string, PageView> = new Map();
  private logger: Logger;

  constructor(logger: Logger) {
    this.logger = logger;
    this.logger.info('InMemoryPageViewRepository initialized.');
  }

  async save(pageView: PageView): Promise<void> {
    this.logger.debug(`Attempting to save page view: ${pageView.id}`);
    try {
      this.pageViews.set(pageView.id, pageView);
      this.logger.info(`Successfully saved page view: ${pageView.id}`);
    } catch (error) {
      this.logger.error(`Error saving page view ${pageView.id}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async findById(id: string): Promise<PageView | null> {
    this.logger.debug(`Attempting to find page view by ID: ${id}`);
    try {
      const pageView = this.pageViews.get(id) ?? null;
      if (pageView) {
        this.logger.info(`Found page view by ID: ${id}`);
      } else {
        this.logger.warn(`Page view not found for ID: ${id}`);
      }
      return pageView;
    } catch (error) {
      this.logger.error(`Error finding page view by ID ${id}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async findByEntityId(entityType: EntityType, entityId: string): Promise<PageView[]> {
    this.logger.debug(`Attempting to find page views for entityType: ${entityType}, entityId: ${entityId}`);
    try {
      const pageViews = Array.from(this.pageViews.values()).filter(
        pv => pv.entityType === entityType && pv.entityId === entityId
      );
      this.logger.info(`Found ${pageViews.length} page views for entityType: ${entityType}, entityId: ${entityId}`);
      return pageViews;
    } catch (error) {
      this.logger.error(`Error finding page views by entity ID ${entityId}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async findByDateRange(startDate: Date, endDate: Date): Promise<PageView[]> {
    this.logger.debug(`Attempting to find page views by date range: ${startDate.toISOString()} - ${endDate.toISOString()}`);
    try {
      const pageViews = Array.from(this.pageViews.values()).filter(
        pv => pv.timestamp >= startDate && pv.timestamp <= endDate
      );
      this.logger.info(`Found ${pageViews.length} page views for date range.`);
      return pageViews;
    } catch (error) {
      this.logger.error(`Error finding page views by date range:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async findBySession(sessionId: string): Promise<PageView[]> {
    this.logger.debug(`Attempting to find page views by session ID: ${sessionId}`);
    try {
      const pageViews = Array.from(this.pageViews.values()).filter(
        pv => pv.sessionId === sessionId
      );
      this.logger.info(`Found ${pageViews.length} page views for session ID: ${sessionId}`);
      return pageViews;
    } catch (error) {
      this.logger.error(`Error finding page views by session ID ${sessionId}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async countByEntityId(entityType: EntityType, entityId: string, since?: Date): Promise<number> {
    this.logger.debug(`Attempting to count page views for entityType: ${entityType}, entityId: ${entityId}, since: ${since?.toISOString()}`);
    try {
      const count = Array.from(this.pageViews.values()).filter(
        pv => pv.entityType === entityType &&
              pv.entityId === entityId &&
              (!since || pv.timestamp >= since)
      ).length;
      this.logger.info(`Counted ${count} page views for entityType: ${entityType}, entityId: ${entityId}`);
      return count;
    } catch (error) {
      this.logger.error(`Error counting page views by entity ID ${entityId}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async countUniqueVisitors(entityType: EntityType, entityId: string, since?: Date): Promise<number> {
    this.logger.debug(`Attempting to count unique visitors for entityType: ${entityType}, entityId: ${entityId}, since: ${since?.toISOString()}`);
    try {
      const visitors = new Set<string>();
      Array.from(this.pageViews.values())
        .filter(
          pv => pv.entityType === entityType &&
                pv.entityId === entityId &&
                (!since || pv.timestamp >= since)
        )
        .forEach(pv => {
          visitors.add(pv.visitorId ?? pv.sessionId);
        });
      this.logger.info(`Counted ${visitors.size} unique visitors for entityType: ${entityType}, entityId: ${entityId}`);
      return visitors.size;
    } catch (error) {
      this.logger.error(`Error counting unique visitors for entity ID ${entityId}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  // Helper for testing
  clear(): void {
    this.logger.debug('Clearing all page views.');
    this.pageViews.clear();
    this.logger.info('All page views cleared.');
  }

  // Helper for seeding demo data
  seed(pageViews: PageView[]): void {
    this.logger.debug(`Seeding ${pageViews.length} page views.`);
    try {
      pageViews.forEach(pv => this.pageViews.set(pv.id, pv));
      this.logger.info(`Successfully seeded ${pageViews.length} page views.`);
    } catch (error) {
      this.logger.error(`Error seeding page views:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }
}