#!/usr/bin/env python3
import fcntl
import os
import time
import eyed3
import tempfile
import subprocess
from mpd import MPDClient
from pathlib import Path
from typing import Optional, Union
import hashlib

# lock file to prevent parallel runs / duplicate notifications
LOCK_FN = "/tmp/rmpc-notify.lock"
DUPLICATE_WINDOW = 5  # seconds to consider same file a duplicate

exception_buff = []

# Acquire non-blocking exclusive lock; if fails, another instance is running -> exit
_lock_fd = open(LOCK_FN, "a+")
try:
    fcntl.flock(_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    _lock_fd.seek(0)
    _lock_fd.truncate()
    _lock_fd.write(f"{os.getpid()} {time.time()}\n")
    _lock_fd.flush()
except BlockingIOError:
    # Another instance is running — exit silently
    raise SystemExit(0)


_last_shown = {"id": None, "ts": 0}

def file_id(path: Path) -> str:
    try:
        st = path.stat()
        return f"{path}:{st.st_mtime_ns}"
    except Exception:
        return hashlib.sha1(str(path).encode()).hexdigest()

def extract(track: Path) -> Optional[Path]:
    try:
        audio = eyed3.load(track)
    except Exception as e:
        exception_buff.append(f"eyed3.load error: {e}")
        return None

    if not audio or not audio.tag:
        exception_buff.append("track data unavailable")
        return None

    # Защита от проблем с парсингом дат (например "0")
    try:
        rd = getattr(audio.tag, "recording_date", None)
        if rd:
            raw = getattr(rd, "text", None) or str(rd)
            if raw == "0" or raw.strip() == "":
                audio.tag.recording_date = None
    except Exception:
        pass

    images = getattr(audio.tag, "images", None)
    if not images:
        return None

    try:
        with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp:
            temp.write(images[0].image_data)
            return temp.name
    except Exception as e:
        exception_buff.append(f"image extract error: {e}")
        return None

def notify(icon: Union[str, Path], title: str, body: str) -> None:
    try:
        subprocess.Popen(["notify-send", "-i", str(icon), "-a", "rmpc", "-t", "2500", title, body])
    except Exception as e:
        exception_buff.append(e)

def main():
    global _last_shown

    try:
        client = MPDClient()
        client.connect("localhost", 6600)
        song = client.currentsong() or {}
    except Exception as e:
        exception_buff.append(f"mpd connection error: {e}")
        song = {}

    music_path = Path("/home/zloy_linux/Музыка/iTunes/")
    track_rel = song.get('file')
    if not track_rel:
        exception_buff.append("no track file in mpd response")
        track_path = None
    else:
        track_path = music_path / track_rel

    cover_path = None
    if not track_path or not track_path.exists():
        exception_buff.append("track file not found")
    else:
        ident = file_id(track_path)
        now = time.time()
        # дедупликация по файлу+mtime в пределах окна
        if _last_shown["id"] == ident and (now - _last_shown["ts"]) < DUPLICATE_WINDOW:
            return  # пропускаем повторный показ
        cover_path = extract(track_path)
        _last_shown["id"] = ident
        _last_shown["ts"] = now

    artist = song.get('artist', 'Unknown artist')
    title = song.get('title', 'Unknown title')
    body = f"[E] {', '.join(str(e) for e in exception_buff)}" if exception_buff else artist

    notify(cover_path or "audio-x-generic", title, body)

    if cover_path:
        time.sleep(1)
        try:
            os.remove(cover_path)
        except Exception as e:
            # не критично — логим в exception_buff и выводим в stdout
            print(f"[E] cleanup error: {e}")

if __name__ == "__main__":
    try:
        main()
    finally:
        try:
            # release lock by closing descriptor (file remains but unlocked)
            _lock_fd.close()
        except Exception:
            pass

