import { Injectable } from '@nestjs/common';

export interface AttributionInput {
  utmSource?:  string;
  utmMedium?:  string;
  utmCampaign?:  string;
  utmContent?:  string;
  utmTerm?:  string;
  referrer?:  string;
}

export interface ResolvedAttribution {
  source: 'organic' | 'direct' | 'social' | 'paid' | 'email' | 'referral' | 'affiliate';
  rawSource: string | null;
  medium: string | null;
  campaign: string | null;
  content: string | null;
  term: string | null;
  referrer: string | null;
}

@Injectable()
export class AttributionService {
  private readonly socialDomains = new Set([
    'facebook.com',
    'fb.com',
    'twitter.com',
    'x.com',
    't.co',
    'instagram.com',
    'linkedin.com',
    'pinterest.com',
    'tiktok.com',
    'reddit.com',
    'youtube.com',
    'snapchat.com',
    'tumblr.com',
    'medium.com',
  ]);

  private readonly searchEngines = new Set([
    'google.com',
    'google.co.uk',
    'google.de',
    'google.fr',
    'bing.com',
    'yahoo.com',
    'duckduckgo.com',
    'baidu.com',
    'yandex.ru',
    'yandex.com',
  ]);

  /**
   * Resolve traffic source from UTM params and referrer
   * Priority: UTM params > Referrer analysis > Direct
   */
  resolveTrafficSource(input: AttributionInput): ResolvedAttribution {
    const result: ResolvedAttribution = {
      source: 'direct',
      rawSource: input.utmSource ?? null,
      medium: input.utmMedium ?? null,
      campaign: input.utmCampaign ?? null,
      content: input.utmContent ?? null,
      term: input.utmTerm ?? null,
      referrer: input.referrer ?? null,
    };

    // Priority 1: UTM parameters
    if (input.utmSource || input.utmMedium) {
      result.source = this.resolveFromUtm(input);
      return result;
    }

    // Priority 2: Referrer analysis
    if (input.referrer) {
      result.source = this.resolveFromReferrer(input.referrer);
      return result;
    }

    // Default: Direct traffic
    return result;
  }

  /**
   * Resolve source from UTM parameters
   */
  private resolveFromUtm(input: AttributionInput): ResolvedAttribution['source'] {
    const medium = input.utmMedium?.toLowerCase();
    const source = input.utmSource?.toLowerCase();

    // Check medium first
    if (medium) {
      if (medium === 'cpc' || medium === 'ppc' || medium === 'paid' || medium === 'paidsearch') {
        return 'paid';
      }
      if (medium === 'email' || medium === 'newsletter') {
        return 'email';
      }
      if (medium === 'social' || medium === 'social-media') {
        return 'social';
      }
      if (medium === 'affiliate' || medium === 'partner') {
        return 'affiliate';
      }
      if (medium === 'organic') {
        return 'organic';
      }
      if (medium === 'referral') {
        return 'referral';
      }
    }

    // Check source
    if (source) {
      if (this.isSocialDomain(source)) {
        return 'social';
      }
      if (this.isSearchEngine(source)) {
        return medium === 'cpc' ? 'paid' : 'organic';
      }
    }

    return 'referral';
  }

  /**
   * Resolve source from referrer URL
   */
  private resolveFromReferrer(referrer: string): ResolvedAttribution['source'] {
    try {
      const url = new URL(referrer);
      const hostname = url.hostname.replace(/^www\./, '');

      if (this.isSocialDomain(hostname)) {
        return 'social';
      }

      if (this.isSearchEngine(hostname)) {
        return 'organic';
      }

      // Has referrer but not recognized = referral
      return 'referral';
    } catch {
      // Invalid referrer URL
      return 'direct';
    }
  }

  /**
   * Check if domain is a social platform
   */
  private isSocialDomain(domain: string): boolean {
    const normalized = domain.replace(/^www\./, '').toLowerCase();
    return this.socialDomains.has(normalized) ||
      Array.from(this.socialDomains).some((social) => normalized.endsWith(`.${social}`));
  }

  /**
   * Check if domain is a search engine
   */
  private isSearchEngine(domain: string): boolean {
    const normalized = domain.replace(/^www\./, '').toLowerCase();
    return this.searchEngines.has(normalized) ||
      Array.from(this.searchEngines).some((engine) => {
        const prefix = engine.split('.')[0];
        return prefix !== undefined && normalized.includes(prefix);
      });
  }
}
