import { Logger } from '../utils/Logger';

interface FingerprintConfig {
  userAgent: string;
  screenResolution: string;
  timezone: string;
  language: string;
  platform: string;
  hardwareConcurrency: number;
  deviceMemory: number;
  colorDepth: number;
  pixelRatio: number;
  fonts: string[];
  canvasNoise: number;
  webglNoise: number;
}

interface ProxyConfig {
  host: string;
  port: number;
  username?: string;
  password?: string;
  protocol: 'http' | 'https' | 'socks5';
  country?: string;
  region?: string;
}

interface IsolatedContext {
  shopId: string;
  fingerprint: FingerprintConfig;
  proxy: ProxyConfig;
  cookies: Record<string, string>;
  localStorage: Record<string, string>;
  sessionStorage: Record<string, string>;
  createdAt: string;
  lastUsedAt: string;
  useCount: number;
}

interface ContextCreationResult {
  success: boolean;
  context?: IsolatedContext;
  error?: string;
  traceId: string;
}

export class FingerprintManager {
  private logger = new Logger('FingerprintManager');
  private contexts: Map<string, IsolatedContext> = new Map();
  private readonly USER_AGENT_TEMPLATES = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.0',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15',
  ];

  private readonly TIMEZONES = [
    'America/New_York',
    'America/Los_Angeles',
    'Europe/London',
    'Europe/Paris',
    'Asia/Tokyo',
    'Asia/Shanghai',
    'Asia/Singapore',
    'Australia/Sydney',
  ];

  private readonly LANGUAGES = [
    'en-US,en;q=0.9',
    'en-GB,en;q=0.9',
    'zh-CN,zh;q=0.9,en;q=0.8',
    'ja-JP,ja;q=0.9,en;q=0.8',
    'de-DE,de;q=0.9,en;q=0.8',
    'fr-FR,fr;q=0.9,en;q=0.8',
  ];

  private readonly SCREEN_RESOLUTIONS = [
    '1920x1080',
    '1366x768',
    '1440x900',
    '1536x864',
    '1280x720',
    '2560x1440',
    '1680x1050',
  ];

  async createIsolatedContext(
    shopId: string,
    proxyConfig?: ProxyConfig,
    traceId?: string
  ): Promise<ContextCreationResult> {
    const tid = traceId || this.generateTraceId();
    this.logger.info('Creating isolated context', { shopId, traceId: tid });

    try {
      if (this.contexts.has(shopId)) {
        this.logger.info('Reusing existing context', { shopId, traceId: tid });
        const existing = this.contexts.get(shopId)!;
        existing.lastUsedAt = new Date().toISOString();
        existing.useCount++;
        return {
          success: true,
          context: existing,
          traceId: tid,
        };
      }

      const fingerprint = this.generateFingerprint();
      const proxy = proxyConfig || await this.getProxyForShop(shopId);

      const context: IsolatedContext = {
        shopId,
        fingerprint,
        proxy,
        cookies: {},
        localStorage: {},
        sessionStorage: {},
        createdAt: new Date().toISOString(),
        lastUsedAt: new Date().toISOString(),
        useCount: 1,
      };

      this.contexts.set(shopId, context);

      this.logger.info('Isolated context created', {
        shopId,
        userAgent: fingerprint.userAgent.substring(0, 50) + '...',
        proxy: `${proxy.protocol}://${proxy.host}:${proxy.port}`,
        traceId: tid,
      });

      return {
        success: true,
        context,
        traceId: tid,
      };
    } catch (error: any) {
      this.logger.error('Failed to create isolated context', {
        shopId,
        error: error.message,
        traceId: tid,
      });

      return {
        success: false,
        error: error.message,
        traceId: tid,
      };
    }
  }

  async destroyContext(shopId: string, traceId?: string): Promise<boolean> {
    const tid = traceId || this.generateTraceId();
    this.logger.info('Destroying context', { shopId, traceId: tid });

    const context = this.contexts.get(shopId);
    if (!context) {
      this.logger.warn('Context not found', { shopId, traceId: tid });
      return false;
    }

    try {
      this.contexts.delete(shopId);
      this.logger.info('Context destroyed', { shopId, traceId: tid });
      return true;
    } catch (error: any) {
      this.logger.error('Failed to destroy context', {
        shopId,
        error: error.message,
        traceId: tid,
      });
      return false;
    }
  }

  getContext(shopId: string): IsolatedContext | undefined {
    return this.contexts.get(shopId);
  }

  updateContextCookies(
    shopId: string,
    cookies: Record<string, string>,
    traceId?: string
  ): boolean {
    const tid = traceId || this.generateTraceId();
    const context = this.contexts.get(shopId);

    if (!context) {
      this.logger.warn('Cannot update cookies - context not found', { shopId, traceId: tid });
      return false;
    }

    context.cookies = { ...context.cookies, ...cookies };
    context.lastUsedAt = new Date().toISOString();

    this.logger.info('Cookies updated', { shopId, cookieCount: Object.keys(cookies).length, traceId: tid });
    return true;
  }

  updateContextStorage(
    shopId: string,
    localStorage: Record<string, string>,
    sessionStorage: Record<string, string>,
    traceId?: string
  ): boolean {
    const tid = traceId || this.generateTraceId();
    const context = this.contexts.get(shopId);

    if (!context) {
      this.logger.warn('Cannot update storage - context not found', { shopId, traceId: tid });
      return false;
    }

    context.localStorage = { ...context.localStorage, ...localStorage };
    context.sessionStorage = { ...context.sessionStorage, ...sessionStorage };
    context.lastUsedAt = new Date().toISOString();

    this.logger.info('Storage updated', { shopId, traceId: tid });
    return true;
  }

  getAllActiveContexts(): IsolatedContext[] {
    return Array.from(this.contexts.values());
  }

  cleanupInactiveContexts(maxAgeMinutes: number = 30): number {
    const now = new Date().getTime();
    let cleaned = 0;

    for (const [shopId, context] of this.contexts.entries()) {
      const lastUsed = new Date(context.lastUsedAt).getTime();
      const ageMinutes = (now - lastUsed) / (1000 * 60);

      if (ageMinutes > maxAgeMinutes) {
        this.contexts.delete(shopId);
        cleaned++;
        this.logger.info('Cleaned up inactive context', { shopId, ageMinutes });
      }
    }

    return cleaned;
  }

  private generateFingerprint(): FingerprintConfig {
    const userAgent = this.USER_AGENT_TEMPLATES[
      Math.floor(Math.random() * this.USER_AGENT_TEMPLATES.length)
    ];

    const screenResolution = this.SCREEN_RESOLUTIONS[
      Math.floor(Math.random() * this.SCREEN_RESOLUTIONS.length)
    ];

    const timezone = this.TIMEZONES[
      Math.floor(Math.random() * this.TIMEZONES.length)
    ];

    const language = this.LANGUAGES[
      Math.floor(Math.random() * this.LANGUAGES.length)
    ];

    return {
      userAgent,
      screenResolution,
      timezone,
      language,
      platform: userAgent.includes('Mac') ? 'MacIntel' : 'Win32',
      hardwareConcurrency: [2, 4, 6, 8][Math.floor(Math.random() * 4)],
      deviceMemory: [4, 8, 16][Math.floor(Math.random() * 3)],
      colorDepth: 24,
      pixelRatio: [1, 1.25, 1.5, 2][Math.floor(Math.random() * 4)],
      fonts: ['Arial', 'Times New Roman', 'Helvetica', 'Georgia'],
      canvasNoise: Math.random() * 0.02 - 0.01,
      webglNoise: Math.random() * 0.02 - 0.01,
    };
  }

  private async getProxyForShop(shopId: string): Promise<ProxyConfig> {
    this.logger.info('Fetching proxy for shop', { shopId });

    return {
      host: 'proxy.crawlful.com',
      port: 8080,
      protocol: 'http',
      country: 'US',
    };
  }

  private generateTraceId(): string {
    return `trace-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }
}
