import type { AnalyticsEventProperties, AnalyticsService } from './analytics-service';
import { config } from '../../config';
import type { LoggerService } from '../logging/logger-service';

/**
 * Configuration options for UmamiAnalyticsService.
 *
 * @property enabled - Whether analytics are enabled
 */
export type UmamiAnalyticsServiceOptions = {
  enabled: boolean;
};

/**
 * Umami Analytics Service Implementation (Script-less/Proxy edition).
 *
 * This version implements the Umami tracking protocol directly via fetch,
 * eliminating the need to load an external script.js file.
 *
 * In the browser, it gathers standard metadata (screen, language, referrer)
 * and sends it to the proxied '/stats/api/send' endpoint.
 * On the server, it sends directly to the internal Umami API.
 */
export class UmamiAnalyticsService implements AnalyticsService {
  private websiteId?: string;
  private endpoint: string;
  private logger: LoggerService;
  private serverContext?: {
    userAgent?: string;
    language?: string;
    referrer?: string;
    ip?: string;
  };

  constructor(
    private readonly options: UmamiAnalyticsServiceOptions,
    logger: LoggerService,
  ) {
    this.websiteId = config.analytics.umami.websiteId;
    this.logger = logger.child({ component: 'analytics-umami' });

    // On server, use the full internal URL; on client, use the proxied path
    this.endpoint = typeof window === 'undefined' ? config.analytics.umami.apiEndpoint : '/stats';

    this.logger.debug('Umami service initialized', {
      enabled: this.options.enabled,
      websiteId: this.websiteId ? 'configured' : 'not configured (client-side proxy mode)',
      endpoint: this.endpoint,
    });
  }

  /**
   * Set the server-side context for the current request.
   * This allows the service to use real request headers for tracking.
   */
  setServerContext(context: {
    userAgent?: string;
    language?: string;
    referrer?: string;
    ip?: string;
  }) {
    this.serverContext = context;
  }

  /**
   * Internal method to send the payload to Umami API.
   */
  private async sendPayload(type: 'event', data: Record<string, any>) {
    if (!this.options.enabled) return;

    // On the client, we don't need the websiteId (it's injected by the server-side proxy handler).
    // On the server, we need it because we're calling the Umami API directly.
    const isClient = typeof window !== 'undefined';

    if (!isClient && !this.websiteId) {
      this.logger.warn('Umami tracking called on server but no Website ID configured');
      return;
    }

    try {
      const payload = {
        website: this.websiteId,
        hostname: isClient ? window.location.hostname : 'server',
        screen: isClient ? `${window.screen.width}x${window.screen.height}` : undefined,
        language: isClient ? navigator.language : this.serverContext?.language,
        referrer: isClient ? document.referrer : this.serverContext?.referrer,
        title: isClient ? document.title : undefined,
        ...data,
      };

      this.logger.trace('Sending analytics payload', { type, url: data.url });

      // Add a timeout to prevent hanging requests
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), 2000); // 2s timeout

      const headers: Record<string, string> = {
        'Content-Type': 'application/json',
      };

      // Set User-Agent
      if (isClient) {
        headers['User-Agent'] = navigator.userAgent;
      } else if (this.serverContext?.userAgent) {
        headers['User-Agent'] = this.serverContext.userAgent;
      } else {
        headers['User-Agent'] = 'KLZ-Server-Proxy';
      }

      // Forward client IP if available (Umami must be configured to trust this)
      if (this.serverContext?.ip) {
        headers['X-Forwarded-For'] = this.serverContext.ip;
      }

      try {
        const response = await fetch(`${this.endpoint}/api/send`, {
          method: 'POST',
          headers,
          body: JSON.stringify({ type, payload }),
          keepalive: true,
          signal: controller.signal,
        } as any);

        clearTimeout(timeoutId);

        if (!response.ok) {
          const errorText = await response.text();
          this.logger.warn('Umami API responded with error', {
            status: response.status,
            error: errorText.slice(0, 100),
          });
        }
      } catch (fetchError) {
        clearTimeout(timeoutId);
        if ((fetchError as Error).name === 'AbortError') {
          this.logger.error('Umami request timed out');
        } else {
          throw fetchError;
        }
      }
    } catch (error) {
      this.logger.error('Failed to send analytics', {
        error: (error as Error).message,
      });
    }
  }

  /**
   * Track a custom event.
   */
  track(eventName: string, props?: AnalyticsEventProperties) {
    this.sendPayload('event', {
      name: eventName,
      data: props,
      url:
        typeof window !== 'undefined'
          ? window.location.pathname + window.location.search
          : undefined,
    });
  }

  /**
   * Track a pageview.
   */
  trackPageview(url?: string) {
    this.sendPayload('event', {
      url:
        url ||
        (typeof window !== 'undefined'
          ? window.location.pathname + window.location.search
          : undefined),
    });
  }
}
