from __future__ import annotations

from pathlib import Path
import logging
import subprocess

import requests

from .config import AppException, SimpleConfig


class SpeechToText:
    _transcription_url = "https://api.openai.com/v1/audio/transcriptions"
    _chat_url = "https://api.openai.com/v1/chat/completions"
    _fix_prompts = {
        "pl": (
            "Przeanalizuj poniższe zdanie w języku polskim. "
            "Jeśli wydaje sie prawidłowe, zwróć je bez zmian, a jesli nie, "
            "zwróć poprawioną wersję. Masz zwrócić tylko i wyłącznie podane zdanie "
            "- w wersji poprawionej lub zwykłej: "
        ),
        "en": (
            "Analyze the following sentence in English. "
            "If it seems correct, return it unchanged; otherwise return a corrected version. "
            "Return only and exclusively the sentence - corrected or original: "
        ),
    }

    @classmethod
    def process_webm_file(cls, webm_path: Path) -> dict:
        mp3_path = webm_path.with_suffix(".mp3")
        language = SimpleConfig.get_string("language", "pl")
        try:
            cls._convert_to_mp3(webm_path, mp3_path)
            transcript = cls.transcribe(mp3_path, language)

            if SimpleConfig.get_bool("fix-text", True):
                fixed_text = cls._fix_sentence(transcript, language)
            else:
                fixed_text = transcript

            return {
                "transcript": transcript,
                "fixed_text": fixed_text
            }
        except Exception as e:
            raise AppException(f"Nie udało się zamienić dźwięku na tekst: {e}") from e
        finally:
            cls._remove_file_quietly(mp3_path)

    @classmethod
    def transcribe(cls, file_path: Path, language: str) -> str:
        try:
            response = cls._send_transcription_request(file_path, language)

            logging.info(f"Status transkrypcji: {response.status_code}")
            logging.info(f"Body transkrypcji: {response.text}")

            response.raise_for_status()
            payload = response.json()
            return payload["text"]
        except Exception as e:
            raise AppException(f"Błąd transkrypcji: {e}") from e

    @classmethod
    def _send_transcription_request(cls, file_path: Path, language: str) -> requests.Response:
        api_key = SimpleConfig.get_string("gpt-key")

        with file_path.open("rb") as audio_file:
            return requests.post(
                cls._transcription_url,
                headers={
                    "Authorization": f"Bearer {api_key}",
                },
                data={
                    "model": "whisper-1",
                    "language": language,
                },
                files={
                    "file": (file_path.name, audio_file, "audio/mpeg"),
                },
                timeout=300,
            )

    @classmethod
    def _fix_sentence(cls, text: str, language: str = "pl") -> str:
        api_key = SimpleConfig.get_string("gpt-key")
        model = SimpleConfig.get_string("fix-model", "gpt-4o-mini")

        prompt_prefix = cls._fix_prompts.get(language, cls._fix_prompts["en"])
        prompt = prompt_prefix + text

        response = requests.post(
            cls._chat_url,
            headers={
                "Authorization": f"Bearer {api_key}",
                "Content-Type": "application/json",
            },
            json={
                "model": model,
                "messages": [
                    {
                        "role": "user",
                        "content": prompt
                    }
                ]
            },
            timeout=300,
        )

        logging.info(f"Status poprawki: {response.status_code}")
        logging.info(f"Body poprawki: {response.text}")

        response.raise_for_status()
        payload = response.json()
        return payload["choices"][0]["message"]["content"].strip()

    @staticmethod
    def _convert_to_mp3(source_file: Path, target_file: Path) -> None:
        try:
            subprocess.run(
                [
                    "ffmpeg",
                    "-y",
                    "-i", str(source_file),
                    str(target_file),
                ],
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
            )
        except subprocess.CalledProcessError as e:
            raise AppException(f"Nie udało się przekonwertować pliku audio: {e.stderr}") from e
        except FileNotFoundError as e:
            raise AppException("Nie znaleziono programu ffmpeg") from e

    @staticmethod
    def _remove_file_quietly(path: Path) -> None:
        try:
            if path.exists():
                path.unlink()
        except Exception as e:
            logging.warning(f"Nie udało się usunąć pliku tymczasowego {path}: {e}")