/**
 * Transcription routes — provider-agnostic speech-to-text.
 *
 * POST /transcribe — Download audio from URL, transcribe via configured provider.
 * Product-agnostic: any product backend can call this endpoint.
 *
 * Provider selection: STT_PROVIDER env var (openai | azure | mock).
 * Auto-detects from available credentials if not explicit.
 */

import type { FastifyInstance } from 'fastify';
import { BadRequestError } from '@bytelyst/errors';
import {
  TranscribeRequestSchema,
  MAX_AUDIO_SIZE_BYTES,
  SUPPORTED_AUDIO_TYPES,
  deriveFilename,
  type TranscribeResponse,
} from './types.js';
import { getSTT } from './factory.js';

export async function transcribeRoutes(app: FastifyInstance): Promise<void> {
  /**
   * POST /transcribe — Transcribe audio from a URL.
   *
   * Flow:
   * 1. Validate request (audioUrl, optional language/model/prompt)
   * 2. Check provider is configured
   * 3. Download audio from the provided URL
   * 4. Delegate to configured TranscriptionProvider
   * 5. Return transcribed text + metadata
   */
  app.post('/transcribe', async (req, reply) => {
    const parsed = TranscribeRequestSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }

    const provider = getSTT();

    if (!provider.isConfigured()) {
      return reply.status(503).send({
        error: 'Transcription not available — no STT provider configured',
      });
    }

    const { audioUrl, model, language, prompt, productId } = parsed.data;
    const requestId = req.headers['x-request-id'] as string | undefined;

    req.log.info(
      { audioUrl: audioUrl.substring(0, 80), language, productId },
      'transcription request'
    );

    const startMs = Date.now();

    // ── Step 1: Download audio from URL ──────────────────────────
    let audioBuffer: ArrayBuffer;
    let contentType: string | null;
    try {
      const audioResponse = await fetch(audioUrl, {
        signal: AbortSignal.timeout(30_000),
      });

      if (!audioResponse.ok) {
        throw new BadRequestError(`Failed to download audio: HTTP ${audioResponse.status}`);
      }

      contentType = audioResponse.headers.get('content-type');

      // Check size via Content-Length header first (fast fail)
      const contentLength = audioResponse.headers.get('content-length');
      if (contentLength && parseInt(contentLength, 10) > MAX_AUDIO_SIZE_BYTES) {
        throw new BadRequestError(
          `Audio file too large (${Math.round(parseInt(contentLength, 10) / 1024 / 1024)}MB). Maximum is 25MB.`
        );
      }

      audioBuffer = await audioResponse.arrayBuffer();

      if (audioBuffer.byteLength > MAX_AUDIO_SIZE_BYTES) {
        throw new BadRequestError(
          `Audio file too large (${Math.round(audioBuffer.byteLength / 1024 / 1024)}MB). Maximum is 25MB.`
        );
      }

      if (audioBuffer.byteLength === 0) {
        throw new BadRequestError('Audio file is empty');
      }
    } catch (err) {
      if (err instanceof BadRequestError) throw err;
      const message = err instanceof Error ? err.message : 'Audio download failed';
      req.log.error({ error: message }, 'audio download failed');
      throw new BadRequestError(`Failed to download audio: ${message}`);
    }

    // ── Step 2: Validate audio content-type ─────────────────────
    if (contentType) {
      const baseType = contentType.split(';')[0].trim().toLowerCase();
      if (
        baseType &&
        !SUPPORTED_AUDIO_TYPES.has(baseType) &&
        !baseType.startsWith('application/octet-stream')
      ) {
        throw new BadRequestError(
          `Unsupported audio format: ${baseType}. Supported: ${[...SUPPORTED_AUDIO_TYPES].join(', ')}`
        );
      }
    }

    // ── Step 3: Determine filename from URL or content-type ──────
    const filename = deriveFilename(audioUrl, contentType);

    // ── Step 4: Delegate to provider ─────────────────────────────
    let result: TranscribeResponse;
    try {
      const sttResult = await provider.transcribe({
        audio: audioBuffer,
        filename,
        language,
        prompt,
        model,
      });

      const durationMs = Date.now() - startMs;

      req.log.info(
        {
          durationMs,
          model: sttResult.model,
          language: sttResult.language,
          audioSizeBytes: audioBuffer.byteLength,
          textLength: sttResult.text.length,
          productId,
        },
        'transcription complete'
      );

      result = {
        text: sttResult.text,
        language: sttResult.language,
        durationSeconds: sttResult.durationSeconds,
        model: sttResult.model,
        durationMs,
        requestId,
      };
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Transcription failed';
      req.log.error({ error: message }, 'transcription provider failed');

      if (message.includes('429') || message.includes('rate limit')) {
        return reply.status(429).send({
          error: 'Transcription rate limit exceeded',
          detail: message,
          retryAfter: 60,
        });
      }

      return reply.status(502).send({
        error: 'Transcription failed',
        detail: message,
      });
    }

    return reply.send(result);
  });
}
