import { Injectable, Logger } from "@nestjs/common";
import axios from "axios";
import { EstadoLead } from "../leads/lead.entity";

@Injectable()
export class MediaService {
  private readonly logger = new Logger(MediaService.name);

  private readonly OPENROUTER_URL =
    "https://openrouter.ai/api/v1/chat/completions";

  private get headers() {
    return {
      Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
      "Content-Type": "application/json",
      "HTTP-Referer": "https://reformix.es",
      "X-Title": "Reformix Luisa Bot",
    };
  }

  private getModeloTranscripcion(): string {
    return (
      process.env.MODEL_TRANSCRIPCION || "google/gemini-2.5-flash"
    );
  }

  /**
   * Convierte mimetype de WhatsApp al formato que espera OpenRouter input_audio.
   */
  mimeToAudioFormat(mimeType: string): string {
    const base = mimeType.toLowerCase().split(";")[0].trim();
    const map: Record<string, string> = {
      "audio/ogg": "ogg",
      "audio/opus": "ogg",
      "audio/mpeg": "mp3",
      "audio/mp3": "mp3",
      "audio/mp4": "m4a",
      "audio/aac": "aac",
      "audio/wav": "wav",
      "audio/webm": "webm",
      "audio/flac": "flac",
    };
    return map[base] ?? "ogg";
  }

  /**
   * Elimina encabezados y formato que el modelo pueda añadir a la transcripcion.
   */
  limpiarTranscripcion(texto: string): string {
    return texto
      .replace(/^#+\s*transcripci[oó]n\s*:?\s*\n?/gim, "")
      .replace(/^\*\*transcripci[oó]n\*\*\s*:?\s*\n?/gim, "")
      .replace(/^transcripci[oó]n\s*:?\s*\n?/gim, "")
      .replace(/^```[\s\S]*?\n/g, "")
      .replace(/\n```$/g, "")
      .replace(/^["']|["']$/g, "")
      .trim();
  }

  private detectarFormatoPorMagicBytes(buffer: Buffer): string | null {
    if (
      buffer.length >= 4 &&
      buffer.subarray(0, 4).toString("ascii") === "OggS"
    ) {
      return "ogg";
    }
    if (
      buffer.length >= 3 &&
      buffer[0] === 0xff &&
      (buffer[1] & 0xe0) === 0xe0
    ) {
      return "mp3";
    }
    if (
      buffer.length >= 12 &&
      buffer.subarray(0, 4).toString("ascii") === "RIFF" &&
      buffer.subarray(8, 12).toString("ascii") === "WAVE"
    ) {
      return "wav";
    }
    return null;
  }

  /**
   * Transcribe un audio via OpenRouter input_audio (Gemini por defecto).
   * Claude no soporta audio en OpenRouter; Luisa sigue usando Claude en el resto del pipeline.
   */
  async transcribirAudio(
    audioBuffer: Buffer,
    mimeType = "audio/ogg; codecs=opus",
  ): Promise<string> {
    const FALLBACK =
      "No te he oido bien, me lo repites?";

    const formatFromMime = this.mimeToAudioFormat(mimeType);
    const formatFromMagic = this.detectarFormatoPorMagicBytes(audioBuffer);
    const format = formatFromMagic ?? formatFromMime;
    const base64Audio = audioBuffer.toString("base64");
    const model = this.getModeloTranscripcion();

    this.logger.log(
      `[AUDIO 2/4] MediaService.transcribirAudio — buffer=${audioBuffer.length} bytes, mime=${mimeType}, format=${format}, magic=${formatFromMagic ?? "no detectado"}, base64=${base64Audio.length} chars, modelo=${model}`,
    );

    if (audioBuffer.length < 100) {
      this.logger.warn(
        `[AUDIO 2/4] Buffer demasiado pequeno (${audioBuffer.length} bytes), abortando transcripcion`,
      );
      return FALLBACK;
    }

    const systemPrompt =
      "Eres un transcriptor de voz para usuarios de Madrid y Espana. " +
      "Transcribe en espanol peninsular tal como se habla, conservando coloquialismos, " +
      "muletillas y jerga (vale, tio, guay, mola, etc.) sin corregir ni formalizar. " +
      "Responde unicamente con las palabras dichas, sin titulos, markdown, comillas ni explicaciones.";

    const userPrompt =
      "Transcribe exactamente lo que dice la persona en este audio. " +
      "Es espanol de Espana, posiblemente con tono coloquial madrileño. " +
      "Devuelve solo las palabras habladas, tal cual, nada mas.";

    try {
      const payload = {
        model,
        messages: [
          { role: "system", content: systemPrompt },
          {
            role: "user",
            content: [
              { type: "text", text: userPrompt },
              {
                type: "input_audio",
                input_audio: {
                  data: base64Audio,
                  format,
                },
              },
            ],
          },
        ],
        max_tokens: 512,
        temperature: 0,
      };

      this.logger.debug(
        `[AUDIO 3/4] Enviando a OpenRouter — endpoint=${this.OPENROUTER_URL}, content_type=input_audio, format=${format}`,
      );

      const response = await axios.post(this.OPENROUTER_URL, payload, {
        headers: this.headers,
      });

      const raw: string =
        response.data.choices?.[0]?.message?.content?.trim() ?? "";
      const modeloUsado = response.data.model ?? model;

      this.logger.log(
        `[AUDIO 3/4] Respuesta OpenRouter — modelo=${modeloUsado}, raw_length=${raw.length}, raw_preview="${raw.slice(0, 120).replace(/\n/g, "\\n")}"`,
      );

      if (!raw) {
        this.logger.warn(
          "[AUDIO 4/4] Modelo devolvio respuesta vacia para el audio",
        );
        return FALLBACK;
      }

      const transcripcion = this.limpiarTranscripcion(raw);

      this.logger.log(
        `[AUDIO 4/4] Transcripcion final — length=${transcripcion.length}, texto="${transcripcion.slice(0, 200).replace(/\n/g, "\\n")}"`,
      );

      if (!transcripcion) {
        this.logger.warn(
          "[AUDIO 4/4] Transcripcion vacia tras limpieza, usando fallback",
        );
        return FALLBACK;
      }

      return transcripcion;
    } catch (error) {
      this.logger.error(
        `[AUDIO 3/4] Error transcribiendo audio: ${error.message}`,
        error.response?.data,
      );
      return FALLBACK;
    }
  }

  /**
   * Infiere informacion de una imagen segun el estado actual del lead.
   */
  async inferirImagen(
    imagenBuffer: Buffer,
    mimeType = "image/jpeg",
    estadoActual: EstadoLead = "en_proceso",
  ): Promise<string> {
    const FALLBACK =
      "Recibi tu imagen pero no pude analizarla bien. Puedes describirme lo que muestra?";

    const promptPorEstado: Record<string, string> = {
      nuevo:
        "Describe brevemente que tipo de espacio se ve en esta imagen y sus caracteristicas principales.",
      en_proceso:
        "Describe el espacio que aparece en la imagen: tipo de habitacion, materiales, estado actual, tamano aproximado.",
      recopilando_datos:
        "Analiza esta imagen de un espacio para reformar. Indica: tipo de espacio, metros cuadrados aproximados, materiales visibles, estilo actual, estado de conservacion.",
      completado:
        "Describe lo que ves en esta imagen relacionado con reformas o diseno de interiores.",
      no_viable: "Describe brevemente que muestra esta imagen.",
      perdido: "Describe brevemente que muestra esta imagen.",
    };

    const promptDeVision =
      promptPorEstado[estadoActual] ||
      "Describe que ves en esta imagen en el contexto de una reforma de hogar.";

    try {
      const base64Imagen = imagenBuffer.toString("base64");

      const response = await axios.post(
        this.OPENROUTER_URL,
        {
          model:
            process.env.MODEL_GENERADOR ||
            process.env.MODEL ||
            "anthropic/claude-sonnet-4-5",
          messages: [
            {
              role: "user",
              content: [
                { type: "text", text: promptDeVision },
                {
                  type: "image_url",
                  image_url: {
                    url: `data:${mimeType};base64,${base64Imagen}`,
                  },
                },
              ],
            },
          ],
          max_tokens: 512,
        },
        { headers: this.headers },
      );

      const inferencia: string =
        response.data.choices?.[0]?.message?.content?.trim();

      if (!inferencia) {
        this.logger.warn("Claude devolvio respuesta vacia para la imagen");
        return FALLBACK;
      }

      this.logger.log(
        `Imagen inferida correctamente (${inferencia.length} chars)`,
      );
      return inferencia;
    } catch (error) {
      this.logger.error(
        `Error analizando imagen: ${error.message}`,
        error.response?.data,
      );
      return FALLBACK;
    }
  }
}
