import { BatchQueue } from './batch-queue';
import { getDeviceData, type CollectedDeviceData } from './device-collector';
import { captureAttribution, type StoredAttribution } from './utm-extractor';

import type {
  AnalyticsConfig,
  AttributionData,
  BatchedEvent,
  ViewEventData,
  EngagementEventData,
  InteractionEvent,
  InteractionEventPayload,
} from './types';

// Type declarations for browser APIs (allows compilation in Node.js environments)
declare const window: {
  addEventListener: (event: string, handler: () => void) => void;
  location: { hostname: string; href: string };
} | undefined;

export class AnalyticsClient {
  private config: Required<AnalyticsConfig>;
  private queue: BatchQueue | null = null;
  private sessionId: string;
  private interactionQueue: InteractionEventPayload[] = [];
  private interactionFlushTimer: ReturnType<typeof setInterval> | null = null;
  private deviceData: CollectedDeviceData | null = null;
  private attribution: StoredAttribution | null = null;

  constructor(config: AnalyticsConfig) {
    this.config = {
      batchSize: 10,
      batchInterval: 5000,
      enableDebugLogging: false,
      enabled: true,
      scrollTracking: { enabled: false },
      trackResizes: false,
      resizeDebounceMs: 1000,
      writeKey: undefined,
      ...config,
    };

    // Skip initialization if analytics is disabled
    if (!this.config.enabled) {
      this.sessionId = '';
      if (this.config.enableDebugLogging) {
        console.log('[Analytics] Disabled via config');
      }
      return;
    }

    // CONSENT-FREE: Generate ephemeral session ID (in-memory only)
    this.sessionId = this.generateSessionId();
    this.deviceData = getDeviceData(); // Collect device data once per session
    this.attribution = captureAttribution(); // Capture UTM params on first load
    this.queue = new BatchQueue(
      this.config.batchSize,
      this.config.batchInterval,
      this.flushBatch.bind(this),
      this.config.enableDebugLogging,
    );

    // Flush on page unload
    if (typeof window !== 'undefined') {
      window.addEventListener('beforeunload', () => {
        this.flush();
      });
    }

    // Set up periodic interaction flush
    this.interactionFlushTimer = setInterval(() => {
      this.flushInteractions();
    }, this.config.batchInterval);
  }

  /**
   * Ensure queue is initialized. Throws if analytics is disabled.
   */
  private ensureQueue(): BatchQueue {
    if (!this.queue) {
      throw new Error('Analytics client not initialized (disabled or not configured)');
    }
    return this.queue;
  }

  /**
   * Get current attribution data.
   */
  getAttribution(): AttributionData | null {
    if (!this.attribution) {
      return null;
    }
    return {
      utmSource: this.attribution.utmSource,
      utmMedium: this.attribution.utmMedium,
      utmCampaign: this.attribution.utmCampaign,
      utmContent: this.attribution.utmContent,
      utmTerm: this.attribution.utmTerm,
    };
  }

  trackView(data: Omit<ViewEventData, 'app' | 'sessionId'>): void {
    if (!this.config.enabled) {return;}

    // Include attribution on first view (or all views for completeness)
    const attribution = this.getAttribution();

    // Construct ViewEventData with proper types
    const viewData: ViewEventData = {
      ...data,
      app: this.config.appName,
      sessionId: this.sessionId,
      // Include client device data for server-side enrichment
      clientDevice: this.deviceData ?? undefined,
      // Include attribution data for first-touch tracking
      attribution: attribution ?? undefined,
    };

    const event: BatchedEvent = {
      type: 'view',
      data: viewData,
      timestamp: Date.now(),
    };

    this.ensureQueue().add(event);
  }

  trackEngagement(data: EngagementEventData): void {
    if (!this.config.enabled) {return;}

    const event: BatchedEvent = {
      type: 'engagement',
      data,
      timestamp: Date.now(),
    };

    this.ensureQueue().add(event);
  }

  trackInteraction(event: InteractionEvent): void {
    if (!this.config.enabled) {return;}

    const payload: InteractionEventPayload = {
      type: event.type,
      data: event.data,
      sessionId: this.sessionId,
      timestamp: Date.now(),
    };

    this.interactionQueue.push(payload);

    // Flush if batch size reached
    if (this.interactionQueue.length >= this.config.batchSize) {
      this.flushInteractions();
    }
  }

  async flush(): Promise<void> {
    if (!this.config.enabled) {return;}
    await Promise.all([
      this.ensureQueue().flush(),
      this.flushInteractions(),
    ]);
  }

  destroy(): void {
    if (!this.config.enabled) {return;}
    this.ensureQueue().destroy();
    if (this.interactionFlushTimer) {
      clearInterval(this.interactionFlushTimer);
      this.interactionFlushTimer = null;
    }
  }

  private async flushInteractions(): Promise<void> {
    if (this.interactionQueue.length === 0) {return;}

    const events = [...this.interactionQueue];
    this.interactionQueue = [];

    try {
      await fetch(`${this.config.apiBaseUrl}/analytics/track/interaction`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...(this.config.writeKey ? { 'X-Write-Key': this.config.writeKey } : {}),
        },
        body: JSON.stringify({ events }),
      });

      if (this.config.enableDebugLogging) {
        console.log(`[Analytics] Flushed ${events.length} interaction events`);
      }
    } catch (error) {
      // Re-queue on failure
      this.interactionQueue = [...events, ...this.interactionQueue];
      if (this.config.enableDebugLogging) {
        console.error('[Analytics] Failed to flush interactions:', error);
      }
    }
  }

  private isViewEvent(event: BatchedEvent): event is BatchedEvent & { type: 'view'; data: ViewEventData } {
    return event.type === 'view';
  }

  private isEngagementEvent(event: BatchedEvent): event is BatchedEvent & { type: 'engagement'; data: EngagementEventData } {
    return event.type === 'engagement';
  }

  private async flushBatch(events: BatchedEvent[]): Promise<void> {
    const viewEvents = events
      .filter(this.isViewEvent)
      .map((e) => e.data);

    const engagementEvents = events
      .filter(this.isEngagementEvent)
      .map((e) => e.data);

    const promises: Promise<void>[] = [];

    if (viewEvents.length > 0) {
      promises.push(this.sendViewEvents(viewEvents));
    }

    if (engagementEvents.length > 0) {
      promises.push(this.sendEngagementEvents(engagementEvents));
    }

    await Promise.all(promises);
  }

  private async sendViewEvents(events: ViewEventData[]): Promise<void> {
    const headers: Record<string, string> = { 'Content-Type': 'application/json' };
    if (this.config.writeKey) headers['X-Write-Key'] = this.config.writeKey;

    for (const event of events) {
      await fetch(`${this.config.apiBaseUrl}/analytics/track/view`, {
        method: 'POST',
        headers,
        body: JSON.stringify(event),
      });
    }
  }

  private async sendEngagementEvents(
    events: EngagementEventData[],
  ): Promise<void> {
    const headers: Record<string, string> = { 'Content-Type': 'application/json' };
    if (this.config.writeKey) headers['X-Write-Key'] = this.config.writeKey;

    for (const event of events) {
      await fetch(`${this.config.apiBaseUrl}/analytics/track/engagement`, {
        method: 'POST',
        headers,
        body: JSON.stringify(event),
      });
    }
  }

  /**
   * Generate ephemeral session ID (consent-free, in-memory only).
   */
  private generateSessionId(): string {
    return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
  }
}
