from __future__ import annotations

import hashlib
import tempfile
from pathlib import Path
from urllib.parse import urlparse

import httpx


AUDIO_HEADERS = {
    "User-Agent": "Mozilla/5.0",
    "Accept": "audio/mpeg,audio/*;q=0.9,*/*;q=0.8",
    "Referer": "https://www.gggmusic.com/",
}


class Player:
    def __init__(self, cache_dir: str | Path | None = None, mixer=None, http_client=None):
        self.cache_dir = Path(cache_dir) if cache_dir is not None else Path(tempfile.gettempdir()) / "sad-tui-player-cache"
        self.cache_dir.mkdir(parents=True, exist_ok=True)
        self.http_client = http_client or httpx.Client(timeout=60, follow_redirects=True, trust_env=False, headers=AUDIO_HEADERS)
        if mixer is None:
            import pygame

            mixer = pygame.mixer
        self.mixer = mixer
        self.current_url: str | None = None
        self.current_file: Path | None = None
        self.is_paused = False
        self._initialized = False
        self._duration_file: Path | None = None
        self._duration_seconds: float | None = None

    def play(self, url: str) -> None:
        self._ensure_initialized()
        path = self._cached_file(url)
        self.mixer.music.load(str(path))
        self.mixer.music.play()
        self.current_url = url
        self.current_file = path
        self._duration_file = None
        self._duration_seconds = None
        self.is_paused = False

    def prefetch(self, url: str) -> Path:
        return self._cached_file(url)

    def pause(self) -> None:
        self._ensure_initialized()
        self.mixer.music.pause()
        self.is_paused = True

    def resume(self) -> None:
        self._ensure_initialized()
        self.mixer.music.unpause()
        self.is_paused = False

    def position_seconds(self) -> float:
        if not self._initialized:
            return 0.0
        get_pos = getattr(self.mixer.music, "get_pos", None)
        if get_pos is None:
            return 0.0
        try:
            value = get_pos()
        except Exception:
            return 0.0
        return max(0.0, value / 1000)

    def duration_seconds(self) -> float | None:
        if not self._initialized or self.current_file is None:
            return None
        if self._duration_file == self.current_file:
            return self._duration_seconds
        sound_factory = getattr(self.mixer, "Sound", None)
        if sound_factory is None:
            return None
        try:
            self._duration_seconds = max(0.0, float(sound_factory(str(self.current_file)).get_length()))
            self._duration_file = self.current_file
            return self._duration_seconds
        except Exception:
            return None

    def is_playing(self) -> bool:
        if not self._initialized:
            return False
        get_busy = getattr(self.mixer.music, "get_busy", None)
        if get_busy is None:
            return False
        try:
            return bool(get_busy())
        except Exception:
            return False

    def toggle_pause(self) -> bool:
        if self.is_paused:
            self.resume()
        else:
            self.pause()
        return self.is_paused

    def stop(self) -> None:
        self._ensure_initialized()
        self.mixer.music.stop()
        self.current_url = None
        self.current_file = None
        self._duration_file = None
        self._duration_seconds = None
        self.is_paused = False

    def _ensure_initialized(self) -> None:
        if self._initialized:
            return
        try:
            self.mixer.init(buffer=4096)
        except TypeError:
            self.mixer.init()
        self._initialized = True

    def _cached_file(self, url: str) -> Path:
        parsed = urlparse(url)
        suffix = Path(parsed.path).suffix or ".audio"
        name = hashlib.sha256(url.encode("utf-8")).hexdigest() + suffix
        target = self.cache_dir / name
        if target.exists() and target.stat().st_size > 0:
            return target
        tmp = target.with_suffix(target.suffix + ".tmp")
        with self.http_client.stream("GET", url) as response:
            response.raise_for_status()
            with tmp.open("wb") as file:
                for chunk in response.iter_bytes():
                    if chunk:
                        file.write(chunk)
        tmp.replace(target)
        return target
