import { Injectable, Logger } from '@nestjs/common';
import { SocksProxyAgent } from 'socks-proxy-agent';
import axios, { AxiosInstance } from 'axios';
import * as net from 'net';
import * as tls from 'tls';

@Injectable()
export class TorService {
  private readonly logger = new Logger(TorService.name);
  private agent: SocksProxyAgent | null = null;
  private axiosInstance: AxiosInstance | null = null;
  private isInitialized = false;
  private identityRotationCounter = 0;

  private readonly config = {
    host: '127.0.0.1',
    port: 9050,
    controlPort: 9051,
    timeout: 30000,
    maxRetries: 5,
    retryDelay: 5000,
    maxIdentityRotations: 3
  };

  constructor() {}

  private async initializeAgent(): Promise<void> {
    if (this.isInitialized) return;

    this.logger.log('Initializing Tor connection...');
    
    const proxyUrl = `socks5://${this.config.host}:${this.config.port}`;
    this.agent = new SocksProxyAgent(proxyUrl, {
      timeout: this.config.timeout,
      lookup: (hostname, options, callback) => {
        callback(null, hostname, 4);
      }
    });

    this.axiosInstance = axios.create({
      httpAgent: this.agent,
      httpsAgent: this.agent,
      timeout: this.config.timeout,
      proxy: false,
      maxRedirects: 2,
      validateStatus: () => true
    });

    this.isInitialized = true;
    this.logger.log('Tor connection initialized');
  }

  async makeRequest(
    url: string, 
    options: any = {}, 
    retryCount = 0
  ): Promise<string> {
    if (!this.isInitialized) {
      await this.initializeAgent();
    }

    try {
      const response = await this.axiosInstance!.get(url, {
        ...options,
        timeout: this.config.timeout
      });

      // Handle blocks and CAPTCHAs
      if (response.status === 403 || /captcha|access denied/i.test(response.data)) {
        if (this.identityRotationCounter < this.config.maxIdentityRotations) {
          this.logger.warn(`Rotating Tor identity due to block (${url})`);
          await this.rotateIdentity();
          return this.makeRequest(url, options, retryCount + 1);
        }
        throw new Error('Blocked by target site after multiple identity rotations');
      }

      return response.data;
    } catch (error) {
      this.logger.warn(`Request failed (${url}): ${error.message}`);
      
      if (retryCount < this.config.maxRetries) {
        this.logger.debug(`Retrying request (${retryCount + 1}/${this.config.maxRetries})`);
        await this.rotateIdentity();
        await this.delay(this.config.retryDelay * (retryCount + 1));
        return this.makeRequest(url, options, retryCount + 1);
      }
      
      throw new Error(`Request failed after ${retryCount + 1} attempts: ${error.message}`);
    }
  }

  private async rotateIdentity(): Promise<void> {
    try {
      this.logger.debug('Requesting new Tor identity');
      
      const socket = net.connect(this.config.controlPort, this.config.host);
      await new Promise<void>((resolve, reject) => {
        socket.on('connect', resolve);
        socket.on('error', reject);
      });

      socket.write('AUTHENTICATE ""\r\n');
      socket.write('SIGNAL NEWNYM\r\n');
      socket.write('QUIT\r\n');
      
      socket.end();
      this.identityRotationCounter++;
      
      this.logger.debug(`New Tor identity activated (rotation #${this.identityRotationCounter})`);
      await this.delay(3000); // Wait for circuit refresh
    } catch (error) {
      this.logger.error('Identity rotation failed:', error.message);
      throw new Error('Tor identity rotation failed');
    }
  }

  async isWorking(): Promise<boolean> {
    try {
      const response = await this.makeRequest('https://check.torproject.org/', {}, 0);
      return response.includes('Congratulations. This browser is configured to use Tor.');
    } catch {
      return false;
    }
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}