/**
 * OpenAISttService
 *
 * Calls the OpenAI Whisper transcriptions API to convert audio buffers to text.
 * Supports the self-signed proxy at 67.223.119.33:8443 by disabling TLS
 * verification via https.request (Node 18 native fetch ignores https.Agent).
 */
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as https from 'https';

@Injectable()
export class OpenAISttService {
  private readonly logger = new Logger(OpenAISttService.name);
  private readonly apiKey: string;
  private readonly baseUrl: string;

  constructor(private readonly configService: ConfigService) {
    // Strip trailing slash and /v1 suffix — we always append /v1/...
    this.baseUrl = this.configService
      .get<string>('OPENAI_BASE_URL', 'https://api.openai.com')
      .replace(/\/$/, '')
      .replace(/\/v1$/, '');
    this.apiKey = this.configService.get<string>('OPENAI_API_KEY', '');
  }

  /**
   * Transcribe an audio buffer to text using OpenAI Whisper.
   *
   * @param audioBuffer  Raw audio bytes (any format Whisper accepts: m4a, webm, mp3, wav, etc.)
   * @param filename     Original filename including extension — Whisper uses this to detect format
   * @param language     BCP-47 language code hint (default: 'zh')
   */
  async transcribe(
    audioBuffer: Buffer,
    filename: string,
    language?: string,
  ): Promise<string> {
    const url = `${this.baseUrl}/v1/audio/transcriptions`;
    this.logger.log(
      `STT: transcribing ${filename} (${audioBuffer.length} bytes) → ${url}`,
    );

    // Build multipart/form-data manually to avoid external dependencies
    const boundary = `----FormBoundary${Date.now().toString(16)}`;
    const parts: Buffer[] = [];

    const appendField = (name: string, value: string) => {
      parts.push(
        Buffer.from(
          `--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`,
        ),
      );
    };

    appendField('model', 'whisper-1');
    if (language) appendField('language', language); // omit → Whisper auto-detects
    appendField('response_format', 'json');

    // File field
    parts.push(
      Buffer.from(
        `--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${filename}"\r\nContent-Type: application/octet-stream\r\n\r\n`,
      ),
    );
    parts.push(audioBuffer);
    parts.push(Buffer.from(`\r\n--${boundary}--\r\n`));

    const body = Buffer.concat(parts);

    // Use https.request so rejectUnauthorized: false works for the self-signed
    // proxy. Node 18 native fetch (undici) does NOT honour https.Agent.
    const parsedUrl = new URL(url);
    const result = await new Promise<{ text: string }>((resolve, reject) => {
      const req = https.request(
        {
          hostname: parsedUrl.hostname,
          port: parsedUrl.port || 443,
          path: parsedUrl.pathname + parsedUrl.search,
          method: 'POST',
          headers: {
            Authorization: `Bearer ${this.apiKey}`,
            'Content-Type': `multipart/form-data; boundary=${boundary}`,
            'Content-Length': body.length,
          },
          rejectUnauthorized: false,
        },
        (res) => {
          const chunks: Buffer[] = [];
          res.on('data', (chunk: Buffer) => chunks.push(chunk));
          res.on('end', () => {
            const text = Buffer.concat(chunks).toString('utf8');
            if (res.statusCode && res.statusCode >= 400) {
              reject(
                new Error(
                  `Whisper STT failed: HTTP ${res.statusCode} — ${text}`,
                ),
              );
            } else {
              try {
                resolve(JSON.parse(text) as { text: string });
              } catch {
                reject(
                  new Error(`Whisper STT: invalid JSON response: ${text}`),
                );
              }
            }
          });
        },
      );
      req.on('error', reject);
      req.write(body);
      req.end();
    });

    this.logger.log(`STT result: "${result.text?.slice(0, 80)}"`);
    return result.text ?? '';
  }
}
