/**
 * Qwen API Client for Multi-AI Brainstorm
 * Integrates with PromptArch's Qwen OAuth service
 */

const DEFAULT_ENDPOINT = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
const PROMPTARCH_PROXY = "https://www.rommark.dev/tools/promptarch/api/qwen/chat";
const CREDENTIALS_PATH = `${process.env.HOME}/.claude/qwen-credentials.json`;

// Qwen OAuth Configuration (from Qwen Code source)
const QWEN_OAUTH_BASE_URL = 'https://chat.qwen.ai';
const QWEN_OAUTH_DEVICE_CODE_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/device/code`;
const QWEN_OAUTH_TOKEN_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/token`;
const QWEN_OAUTH_CLIENT_ID = 'f0304373b74a44d2b584a3fb70ca9e56';
const QWEN_OAUTH_SCOPE = 'openid profile email model.completion';
const QWEN_OAUTH_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';

/**
 * Qwen API Client Class
 */
class QwenClient {
  constructor() {
    this.apiKey = null;
    this.accessToken = null;
    this.refreshToken = null;
    this.tokenExpiresAt = null;
    this.endpoint = DEFAULT_ENDPOINT;
    this.model = "coder-model";
  }

  /**
   * Initialize client with credentials
   */
  async initialize() {
    try {
      const fs = require('fs');
      if (fs.existsSync(CREDENTIALS_PATH)) {
        const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf8'));

        // Handle both API key and OAuth token credentials
        if (credentials.accessToken) {
          this.accessToken = credentials.accessToken;
          this.refreshToken = credentials.refreshToken;
          this.tokenExpiresAt = credentials.tokenExpiresAt;
          this.endpoint = credentials.endpoint || DEFAULT_ENDPOINT;

          // Check if token needs refresh
          if (this.isTokenExpired()) {
            await this.refreshAccessToken();
          }
          return true;
        } else if (credentials.apiKey) {
          this.apiKey = credentials.apiKey;
          this.endpoint = credentials.endpoint || DEFAULT_ENDPOINT;
          return true;
        }
      }
    } catch (error) {
      // No credentials stored yet
    }
    return false;
  }

  /**
   * Check if access token is expired
   */
  isTokenExpired() {
    if (!this.tokenExpiresAt) return false;
    // Add 5 minute buffer before expiration
    return Date.now() >= (this.tokenExpiresAt - 5 * 60 * 1000);
  }

  /**
   * Prompt user for authentication method
   */
  async promptForCredentials() {
    const readline = require('readline');
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    });

    return new Promise((resolve, reject) => {
      rl.question(
        '\n🔐 Choose authentication method:\n' +
        '  1. OAuth (Recommended) - Free 2000 requests/day with qwen.ai account\n' +
        '  2. API Key - Get it at https://help.aliyun.com/zh/dashscope/\n\n' +
        'Enter choice (1 or 2): ',
        async (choice) => {
          rl.close();

          if (choice === '1') {
            try {
              await this.performOAuthFlow();
              resolve(true);
            } catch (error) {
              reject(error);
            }
          } else if (choice === '2') {
            await this.promptForAPIKey();
            resolve(true);
          } else {
            reject(new Error('Invalid choice. Please enter 1 or 2.'));
          }
        }
      );
    });
  }

  /**
   * Prompt user for API key only
   */
  async promptForAPIKey() {
    const readline = require('readline');
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    });

    return new Promise((resolve, reject) => {
      rl.question('Enter your Qwen API key (get it at https://help.aliyun.com/zh/dashscope/): ', (key) => {
        if (!key || key.trim().length === 0) {
          rl.close();
          reject(new Error('API key is required'));
          return;
        }

        this.apiKey = key.trim();
        this.saveCredentials();
        rl.close();
        resolve(true);
      });
    });
  }

  /**
   * Save credentials to file
   */
  saveCredentials() {
    try {
      const fs = require('fs');
      const path = require('path');
      const dir = path.dirname(CREDENTIALS_PATH);

      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }

      const credentials = {
        endpoint: this.endpoint
      };

      // Save OAuth tokens
      if (this.accessToken) {
        credentials.accessToken = this.accessToken;
        credentials.refreshToken = this.refreshToken;
        credentials.tokenExpiresAt = this.tokenExpiresAt;
      }
      // Save API key
      else if (this.apiKey) {
        credentials.apiKey = this.apiKey;
      }

      fs.writeFileSync(
        CREDENTIALS_PATH,
        JSON.stringify(credentials, null, 2)
      );
      console.log(`✓ Credentials saved to ${CREDENTIALS_PATH}`);
    } catch (error) {
      console.warn('Could not save credentials:', error.message);
    }
  }

  /**
   * Generate PKCE code verifier and challenge pair
   */
  generatePKCEPair() {
    const crypto = require('crypto');
    const codeVerifier = crypto.randomBytes(32).toString('base64url');
    const codeChallenge = crypto.createHash('sha256')
      .update(codeVerifier)
      .digest('base64url');
    return { code_verifier: codeVerifier, code_challenge: codeChallenge };
  }

  /**
   * Convert object to URL-encoded form data
   */
  objectToUrlEncoded(data) {
    return Object.keys(data)
      .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
      .join('&');
  }

  /**
   * Perform OAuth 2.0 Device Code Flow (from Qwen Code implementation)
   */
  async performOAuthFlow() {
    const { exec } = require('child_process');

    console.log('\n🔐 Starting Qwen OAuth Device Code Flow...\n');

    // Generate PKCE parameters
    const { code_verifier, code_challenge } = this.generatePKCEPair();

    // Step 1: Request device authorization
    console.log('Requesting device authorization...');
    const deviceAuthResponse = await fetch(QWEN_OAUTH_DEVICE_CODE_ENDPOINT, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Accept': 'application/json',
      },
      body: this.objectToUrlEncoded({
        client_id: QWEN_OAUTH_CLIENT_ID,
        scope: QWEN_OAUTH_SCOPE,
        code_challenge: code_challenge,
        code_challenge_method: 'S256',
      }),
    });

    if (!deviceAuthResponse.ok) {
      const error = await deviceAuthResponse.text();
      throw new Error(`Device authorization failed: ${deviceAuthResponse.status} - ${error}`);
    }

    const deviceAuth = await deviceAuthResponse.json();

    if (!deviceAuth.device_code) {
      throw new Error('Invalid device authorization response');
    }

    // Step 2: Display authorization instructions
    console.log('\n=== Qwen OAuth Device Authorization ===\n');
    console.log('1. Visit this URL in your browser:\n');
    console.log(`   ${deviceAuth.verification_uri_complete}\n`);
    console.log('2. Sign in to your qwen.ai account and authorize\n');
    console.log('Waiting for authorization to complete...\n');

    // Try to open browser automatically
    try {
      const openCommand = process.platform === 'darwin' ? 'open' :
                        process.platform === 'win32' ? 'start' : 'xdg-open';
      exec(`${openCommand} "${deviceAuth.verification_uri_complete}"`, (err) => {
        if (err) {
          console.debug('Could not open browser automatically');
        }
      });
    } catch (err) {
      console.debug('Failed to open browser:', err.message);
    }

    // Step 3: Poll for token
    let pollInterval = 2000; // Start with 2 seconds
    const maxAttempts = Math.ceil(deviceAuth.expires_in / (pollInterval / 1000));
    let attempt = 0;

    while (attempt < maxAttempts) {
      attempt++;

      try {
        console.debug(`Polling for token (attempt ${attempt}/${maxAttempts})...`);

        const tokenResponse = await fetch(QWEN_OAUTH_TOKEN_ENDPOINT, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Accept': 'application/json',
          },
          body: this.objectToUrlEncoded({
            grant_type: QWEN_OAUTH_GRANT_TYPE,
            client_id: QWEN_OAUTH_CLIENT_ID,
            device_code: deviceAuth.device_code,
            code_verifier: code_verifier,
          }),
        });

        // Check for pending authorization (standard OAuth RFC 8628 response)
        if (tokenResponse.status === 400) {
          const errorData = await tokenResponse.json();

          if (errorData.error === 'authorization_pending') {
            // User hasn't authorized yet, continue polling
            await new Promise(resolve => setTimeout(resolve, pollInterval));
            continue;
          }

          if (errorData.error === 'slow_down') {
            // Polling too frequently, increase interval
            pollInterval = Math.min(pollInterval * 1.5, 10000);
            await new Promise(resolve => setTimeout(resolve, pollInterval));
            continue;
          }

          // Other 400 errors (authorization_declined, expired_token, etc.)
          throw new Error(`Authorization failed: ${errorData.error} - ${errorData.error_description || 'No description'}`);
        }

        if (!tokenResponse.ok) {
          const error = await tokenResponse.text();
          throw new Error(`Token request failed: ${tokenResponse.status} - ${error}`);
        }

        // Success! We have the token
        const tokenData = await tokenResponse.json();

        if (!tokenData.access_token) {
          throw new Error('Token response missing access_token');
        }

        // Save credentials
        this.accessToken = tokenData.access_token;
        this.refreshToken = tokenData.refresh_token;
        this.tokenExpiresAt = tokenData.expires_in ?
          Date.now() + (tokenData.expires_in * 1000) : null;

        this.saveCredentials();

        console.log('\n✓ OAuth authentication successful!');
        console.log('✓ Access token obtained and saved.\n');

        return;

      } catch (error) {
        // Check if this is a fatal error (not pending/slow_down)
        if (error.message.includes('Authorization failed') ||
            error.message.includes('Token request failed')) {
          throw error;
        }

        // For other errors, wait and retry
        await new Promise(resolve => setTimeout(resolve, pollInterval));
      }
    }

    throw new Error('OAuth authentication timeout');
  }

  /**
   * Refresh access token using refresh token
   */
  async refreshAccessToken() {
    if (!this.refreshToken) {
      throw new Error('No refresh token available. Please re-authenticate.');
    }

    console.log('🔄 Refreshing access token...');

    const tokenResponse = await fetch(QWEN_OAUTH_TOKEN_ENDPOINT, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: this.objectToUrlEncoded({
        grant_type: 'refresh_token',
        refresh_token: this.refreshToken,
        client_id: QWEN_OAUTH_CLIENT_ID,
      }),
    });

    if (!tokenResponse.ok) {
      const error = await tokenResponse.text();
      throw new Error(`Token refresh failed: ${tokenResponse.status} - ${error}`);
    }

    const tokens = await tokenResponse.json();

    this.accessToken = tokens.access_token;
    if (tokens.refresh_token) {
      this.refreshToken = tokens.refresh_token;
    }
    this.tokenExpiresAt = tokens.expires_in ?
      Date.now() + (tokens.expires_in * 1000) : null;

    this.saveCredentials();
    console.log('✓ Token refreshed successfully');
  }

  /**
   * Get the authentication key (prefer OAuth access token, fallback to API key)
   */
  getAuthKey() {
    return this.accessToken || this.apiKey;
  }

  /**
   * Make a chat completion request
   */
  async chatCompletion(messages, options = {}) {
    const authKey = this.getAuthKey();

    if (!authKey) {
      throw new Error('Qwen API key not configured. Run /multi-ai-brainstorm first to set up.');
    }

    // Check if OAuth token needs refresh
    if (this.accessToken && this.isTokenExpired()) {
      await this.refreshAccessToken();
    }

    const {
      model = this.model,
      stream = false,
      temperature = 0.7,
      maxTokens = 2000
    } = options;

    const payload = {
      model,
      messages,
      stream,
      temperature,
      max_tokens: maxTokens
    };

    try {
      const response = await fetch(PROMPTARCH_PROXY, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${authKey}`
        },
        body: JSON.stringify({
          endpoint: this.endpoint,
          ...payload
        })
      });

      if (!response.ok) {
        const error = await response.text();
        throw new Error(`Qwen API error (${response.status}): ${error}`);
      }

      const data = await response.json();
      return data.choices?.[0]?.message?.content || '';
    } catch (error) {
      if (error.message.includes('fetch')) {
        throw new Error('Network error. Please check your internet connection.');
      }
      throw error;
    }
  }

  /**
   * Check if client is authenticated
   */
  isAuthenticated() {
    return !!(this.accessToken || this.apiKey);
  }
}

// Singleton instance
const client = new QwenClient();

module.exports = client;
