/**
 * Query: GetEntityAnalyticsQuery
 *
 * Retrieves analytics data for an entity (league, driver, team, race).
 * Returns metrics formatted for display to sponsors and admins.
 */

import type { AsyncUseCase } from '@core/shared/application';
import type { Logger } from '@core/shared/application';
import type { IPageViewRepository } from '../repositories/IPageViewRepository';
import type { IEngagementRepository } from '@core/analytics/domain/repositories/IEngagementRepository';
import type { IAnalyticsSnapshotRepository } from '@core/analytics/domain/repositories/IAnalyticsSnapshotRepository';
import type { EntityType } from '../../domain/types/PageView';
import type { SnapshotPeriod } from '../../domain/types/AnalyticsSnapshot';

export interface GetEntityAnalyticsInput {
  entityType: EntityType;
  entityId: string;
  period?: SnapshotPeriod;
  since?: Date;
}

export interface EntityAnalyticsOutput {
  entityType: EntityType;
  entityId: string;
  summary: {
    totalPageViews: number;
    uniqueVisitors: number;
    sponsorClicks: number;
    engagementScore: number;
    trustIndicator: 'high' | 'medium' | 'low';
    exposureValue: number;
  };
  trends: {
    pageViewsChange: number;
    uniqueVisitorsChange: number;
    engagementChange: number;
  };
  period: {
    start: Date;
    end: Date;
    label: string;
  };
}

export class GetEntityAnalyticsQuery
  implements AsyncUseCase<GetEntityAnalyticsInput, EntityAnalyticsOutput> {
  constructor(
    private readonly pageViewRepository: IPageViewRepository,
    private readonly engagementRepository: IEngagementRepository,
    private readonly snapshotRepository: IAnalyticsSnapshotRepository,
    private readonly logger: Logger
  ) {}

  async execute(input: GetEntityAnalyticsInput): Promise<EntityAnalyticsOutput> {
    this.logger.debug(`Executing GetEntityAnalyticsQuery with input: ${JSON.stringify(input)}`);
    const period = input.period ?? 'weekly';
    const now = new Date();
    const since = input.since ?? this.getPeriodStartDate(now, period);
    this.logger.debug(`Calculated period: ${period}, now: ${now.toISOString()}, since: ${since.toISOString()}`);

    // Get current metrics
    let totalPageViews = 0;
    try {
      totalPageViews = await this.pageViewRepository.countByEntityId(
        input.entityType,
        input.entityId,
        since
      );
      this.logger.debug(`Total page views for entity ${input.entityId}: ${totalPageViews}`);
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(`Error counting total page views for entity ${input.entityId}: ${err.message}`);
      throw error;
    }

    let uniqueVisitors = 0;
    try {
      uniqueVisitors = await this.pageViewRepository.countUniqueVisitors(
        input.entityType,
        input.entityId,
        since
      );
      this.logger.debug(`Unique visitors for entity ${input.entityId}: ${uniqueVisitors}`);
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(`Error counting unique visitors for entity ${input.entityId}: ${err.message}`);
      throw error;
    }

    let sponsorClicks = 0;
    try {
      sponsorClicks = await this.engagementRepository.getSponsorClicksForEntity(
        input.entityId,
        since
      );
      this.logger.debug(`Sponsor clicks for entity ${input.entityId}: ${sponsorClicks}`);
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(`Error getting sponsor clicks for entity ${input.entityId}: ${err.message}`);
      throw error;
    }

    // Calculate engagement score (weighted sum of actions)
    let engagementScore = 0;
    try {
      engagementScore = await this.calculateEngagementScore(input.entityId, since);
      this.logger.debug(`Engagement score for entity ${input.entityId}: ${engagementScore}`);
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(`Error calculating engagement score for entity ${input.entityId}: ${err.message}`);
      throw error;
    }
    
    // Determine trust indicator
    const trustIndicator = this.determineTrustIndicator(totalPageViews, uniqueVisitors, engagementScore);
    this.logger.debug(`Trust indicator for entity ${input.entityId}: ${trustIndicator}`);

    // Calculate exposure value (for sponsor ROI)
    const exposureValue = this.calculateExposureValue(totalPageViews, uniqueVisitors, sponsorClicks);
    this.logger.debug(`Exposure value for entity ${input.entityId}: ${exposureValue}`);

    // Get previous period for trends
    const previousPeriodStart = this.getPreviousPeriodStart(since, period);
    this.logger.debug(`Previous period start: ${previousPeriodStart.toISOString()}`);

    let previousPageViews = 0;
    try {
      const fullPreviousPageViews = await this.pageViewRepository.countByEntityId(
        input.entityType,
        input.entityId,
        previousPeriodStart
      );
      previousPageViews = fullPreviousPageViews - totalPageViews; // This calculates change, not just previous period's total
      this.logger.debug(`Previous period full page views: ${fullPreviousPageViews}, change: ${previousPageViews}`);
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(`Error counting previous period page views for entity ${input.entityId}: ${err.message}`);
      throw error;
    }

    let previousUniqueVisitors = 0;
    try {
      const fullPreviousUniqueVisitors = await this.pageViewRepository.countUniqueVisitors(
        input.entityType,
        input.entityId,
        previousPeriodStart
      );
      previousUniqueVisitors = fullPreviousUniqueVisitors - uniqueVisitors; // This calculates change, not just previous period's total
      this.logger.debug(`Previous period full unique visitors: ${fullPreviousUniqueVisitors}, change: ${previousUniqueVisitors}`);

    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(`Error counting previous period unique visitors for entity ${input.entityId}: ${err.message}`);
      throw error;
    }

    const result: EntityAnalyticsOutput = {
      entityType: input.entityType,
      entityId: input.entityId,
      summary: {
        totalPageViews,
        uniqueVisitors,
        sponsorClicks,
        engagementScore,
        trustIndicator,
        exposureValue,
      },
      trends: {
        pageViewsChange: this.calculatePercentageChange(previousPageViews, totalPageViews),
        uniqueVisitorsChange: this.calculatePercentageChange(previousUniqueVisitors, uniqueVisitors),
        engagementChange: 0, // Would need historical engagement data
      },
      period: {
        start: since,
        end: now,
        label: this.formatPeriodLabel(since, now),
      },
    };
    this.logger.info(`Successfully retrieved analytics for entity ${input.entityId}.`);
    return result;
  }

  private getPeriodStartDate(now: Date, period: SnapshotPeriod): Date {
    this.logger.debug(`Calculating period start date for "${period}" from ${now.toISOString()}`);
    const start = new Date(now);
    switch (period) {
      case 'daily':
        start.setDate(start.getDate() - 1);
        break;
      case 'weekly':
        start.setDate(start.getDate() - 7);
        break;
      case 'monthly':
        start.setMonth(start.getMonth() - 1);
        break;
    }
    this.logger.debug(`Period start date calculated: ${start.toISOString()}`);
    return start;
  }

  private getPreviousPeriodStart(currentStart: Date, period: SnapshotPeriod): Date {
    this.logger.debug(`Calculating previous period start date for "${period}" from ${currentStart.toISOString()}`);
    const start = new Date(currentStart);
    switch (period) {
      case 'daily':
        start.setDate(start.getDate() - 1);
        break;
      case 'weekly':
        start.setDate(start.getDate() - 7);
        break;
      case 'monthly':
        start.setMonth(start.getMonth() - 1);
        break;
    }
    this.logger.debug(`Previous period start date calculated: ${start.toISOString()}`);
    return start;
  }

  private async calculateEngagementScore(entityId: string, since: Date): Promise<number> {
    this.logger.debug(`Calculating engagement score for entity ${entityId} since ${since.toISOString()}`);
    let sponsorClicks = 0;
    try {
      sponsorClicks = await this.engagementRepository.getSponsorClicksForEntity(entityId, since);
      this.logger.debug(`Sponsor clicks for engagement score for entity ${entityId}: ${sponsorClicks}`);
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(`Error getting sponsor clicks for engagement score for entity ${entityId}: ${err.message}`);
      throw error;
    }
    const score = sponsorClicks * 10; // Weighted score
    this.logger.debug(`Calculated engagement score for entity ${entityId}: ${score}`);
    return score;
  }

  private determineTrustIndicator(
    pageViews: number,
    uniqueVisitors: number,
    engagementScore: number
  ): 'high' | 'medium' | 'low' {
    this.logger.debug(`Determining trust indicator with pageViews: ${pageViews}, uniqueVisitors: ${uniqueVisitors}, engagementScore: ${engagementScore}`);
    const engagementRate = pageViews > 0 ? engagementScore / pageViews : 0;
    const returningVisitorRate = pageViews > 0 ? (pageViews - uniqueVisitors) / pageViews : 0;
    this.logger.debug(`Engagement rate: ${engagementRate}, Returning visitor rate: ${returningVisitorRate}`);

    if (engagementRate > 0.1 && returningVisitorRate > 0.3) {
      return 'high';
    }
    if (engagementRate > 0.05 || returningVisitorRate > 0.1) {
      return 'medium';
    }
    return 'low';
  }

  private calculateExposureValue(
    pageViews: number,
    uniqueVisitors: number,
    sponsorClicks: number
  ): number {
    this.logger.debug(`Calculating exposure value with pageViews: ${pageViews}, uniqueVisitors: ${uniqueVisitors}, sponsorClicks: ${sponsorClicks}`);
    // Simple exposure value calculation (could be monetized)
    const exposure = (pageViews * 0.01) + (uniqueVisitors * 0.05) + (sponsorClicks * 0.50);
    this.logger.debug(`Calculated exposure value: ${exposure}`);
    return exposure;
  }

  private calculatePercentageChange(previous: number, current: number): number {
    this.logger.debug(`Calculating percentage change from previous: ${previous} to current: ${current}`);
    if (previous === 0) {
      const change = current > 0 ? 100 : 0;
      this.logger.debug(`Percentage change (previous was 0): ${change}%`);
      return change;
    }
    const change = Math.round(((current - previous) / previous) * 100);
    this.logger.debug(`Percentage change: ${change}%`);
    return change;
  }

  private formatPeriodLabel(start: Date, end: Date): string {
    this.logger.debug(`Formatting period label from ${start.toISOString()} to ${end.toISOString()}`);
    const formatter = new Intl.DateTimeFormat('en-US', {
      month: 'short',
      day: 'numeric',
    });
    const label = `${formatter.format(start)} - ${formatter.format(end)}`;
    this.logger.debug(`Formatted period label: "${label}"`);
    return label;
  }
}