const axios = require('axios');
const { logToFile } = require('./logger');
const { getQbittorrentInstances } = require('./config');

class QBittorrentClient {
  constructor(instance) {
    this.id = instance.id;
    this.name = instance.name;
    this.url = instance.url;
    this.username = instance.username;
    this.password = instance.password;
    this.authCookie = null;
  }

  async login() {
    try {
      logToFile(`[qBittorrent:${this.name}] Attempting login...`);
      const response = await axios.post(`${this.url}/api/v2/auth/login`, 
        `username=${encodeURIComponent(this.username)}&password=${encodeURIComponent(this.password)}`,
        {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
          },
          maxRedirects: 0,
          validateStatus: (status) => status >= 200 && status < 400
        }
      );
      
      if (response.headers['set-cookie']) {
        this.authCookie = response.headers['set-cookie'][0];
        logToFile(`[qBittorrent:${this.name}] Login successful`);
        return true;
      }
      
      logToFile(`[qBittorrent:${this.name}] Login failed - no cookie`);
      return false;
    } catch (error) {
      logToFile(`[qBittorrent:${this.name}] Login error: ${error.message}`);
      return false;
    }
  }

  async makeRequest(endpoint, config = {}) {
    const url = `${this.url}${endpoint}`;
    
    if (!this.authCookie) {
      const loggedIn = await this.login();
      if (!loggedIn) {
        throw new Error(`Failed to authenticate with ${this.name}`);
      }
    }
    
    try {
      const response = await axios.get(url, {
        ...config,
        headers: {
          ...config.headers,
          'Cookie': this.authCookie
        }
      });
      return response;
    } catch (error) {
      // If unauthorized, try re-authenticating once
      if (error.response && error.response.status === 403) {
        logToFile(`[qBittorrent:${this.name}] Auth expired, re-authenticating...`);
        this.authCookie = null;
        const loggedIn = await this.login();
        if (loggedIn) {
          return axios.get(url, {
            ...config,
            headers: {
              ...config.headers,
              'Cookie': this.authCookie
            }
          });
        }
      }
      throw error;
    }
  }

  async getTorrents() {
    try {
      const response = await this.makeRequest('/api/v2/torrents/info');
      logToFile(`[qBittorrent:${this.name}] Retrieved ${response.data.length} torrents`);
      // Add instance info to each torrent
      return response.data.map(torrent => ({
        ...torrent,
        instanceId: this.id,
        instanceName: this.name
      }));
    } catch (error) {
      logToFile(`[qBittorrent:${this.name}] Error fetching torrents: ${error.message}`);
      return [];
    }
  }
}

// Persist clients so auth cookies survive between requests
let persistedClients = null;

function getClients() {
  if (persistedClients) return persistedClients;
  const instances = getQbittorrentInstances();
  if (instances.length === 0) {
    logToFile('[qBittorrent] No instances configured');
    return [];
  }
  logToFile(`[qBittorrent] Created ${instances.length} persistent client(s)`);
  persistedClients = instances.map(inst => new QBittorrentClient(inst));
  return persistedClients;
}

async function getAllTorrents() {
  const clients = getClients();
  if (clients.length === 0) {
    return [];
  }
  
  const results = await Promise.all(
    clients.map(client => client.getTorrents().catch(err => {
      logToFile(`[qBittorrent] Error from ${client.name}: ${err.message}`);
      return [];
    }))
  );
  
  const allTorrents = results.flat();
  logToFile(`[qBittorrent] Total torrents from all instances: ${allTorrents.length}`);
  return allTorrents;
}

function formatBytes(bytes) {
  if (bytes === 0) return '0 B';
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

function formatSpeed(bytesPerSecond) {
  return formatBytes(bytesPerSecond) + '/s';
}

function formatEta(seconds) {
  if (seconds < 0 || seconds === 8640000) return '∞'; // qBittorrent uses 8640000 for unknown
  const days = Math.floor(seconds / 86400);
  const hours = Math.floor((seconds % 86400) / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  
  if (days > 0) return `${days}d ${hours}h ${minutes}m`;
  if (hours > 0) return `${hours}h ${minutes}m`;
  return `${minutes}m`;
}

function mapTorrentToDownload(torrent) {
  const totalSize = torrent.size;
  const downloadedSize = torrent.completed;
  const progress = torrent.progress * 100;
  
  // Map qBittorrent states to our status
  const stateMap = {
    'downloading': 'Downloading',
    'stalledDL': 'Downloading',
    'metaDL': 'Downloading',
    'forcedDL': 'Downloading',
    'allocating': 'Downloading',
    'uploading': 'Seeding',
    'stalledUP': 'Seeding',
    'forcedUP': 'Seeding',
    'queuedUP': 'Queued',
    'queuedDL': 'Queued',
    'checkingUP': 'Checking',
    'checkingDL': 'Checking',
    'checkingResumeData': 'Checking',
    'moving': 'Moving',
    'pausedUP': 'Paused',
    'pausedDL': 'Paused',
    'stoppedUP': 'Stopped',
    'stoppedDL': 'Stopped',
    'error': 'Error',
    'missingFiles': 'Error',
    'unknown': 'Unknown'
  };
  
  const status = stateMap[torrent.state] || torrent.state;
  
  return {
    type: 'torrent',
    title: torrent.name,
    instanceName: torrent.instanceName,
    status: status,
    progress: progress.toFixed(1),
    size: formatBytes(totalSize),
    rawSize: totalSize,
    rawDownloaded: downloadedSize,
    speed: formatSpeed(torrent.dlspeed),
    rawSpeed: torrent.dlspeed,
    eta: formatEta(torrent.eta),
    rawEta: torrent.eta,
    seeds: torrent.num_seeds,
    peers: torrent.num_leechs,
    availability: (torrent.availability * 100).toFixed(1),
    hash: torrent.hash,
    category: torrent.category,
    tags: torrent.tags,
    savePath: torrent.content_path || torrent.save_path || null,
    qbittorrent: true
  };
}

module.exports = {
  getTorrents: getAllTorrents,
  getClients,
  mapTorrentToDownload,
  formatBytes,
  formatSpeed,
  formatEta
};
