#!/usr/bin/python3

import os
import sys
import threading
from pathlib import Path

if sys.version_info >= (3, 11):
    from datetime import datetime, UTC
else:
    from datetime import datetime, timezone

from .MPVPlayer import MPVPlayer
from .Event_ts import Event_ts
from .TimerAction import TimerAction

SCRIPT_PATH = Path(os.path.dirname(os.path.realpath(__file__)))


def now_utc_timestamp() -> float:
    if sys.version_info >= (3, 11):
        return datetime.now(UTC).timestamp()
    else:
        return datetime.now(timezone.utc).timestamp()

def now_timestamp() -> float:
    return datetime.now().timestamp()


class PelinkPlayer:
    def __init__(self, file_path:Path):
        self.file_path = file_path
        self.player = None
        self.thread = None
        self.log = (lambda *a, **kw: print("[PelinkPlayer]", *a, **kw))
        self.mpv_kwargs = {
            # "config_dir": str(SCRIPT_PATH.absolute() / "mpvconf"),
        }
        self.event_message = ["", ""] # ["command", "payload"]
        self.message_timer = TimerAction()
        self.commands = {
            'play'      : self.cmd_play,
            'play'      : self.cmd_play,
            'pause'     : self.cmd_pause,
            'seek'      : self.cmd_seek,
            'message'   : self.cmd_message,
            'syncplay'  : self.cmd_syncplay,
            'syncpause' : self.cmd_syncpause,
            'peernew'   : self.cmd_peernew,
            'peerquit'  : self.cmd_peerquit,
        }

    def run_command(self, command, data):
        fun = self.commands.get(command)
        if fun:
            return fun(data)
        else:
            self.log(f"Unknown command {command}")
        return None

    def start_thread(self):
        """
        Run me in a thread
        USAGE:
            event = pelink_player.start_thread(event)
            await event.wait()
            print(pelink_player.get_event_message())
        """
        self.event = Event_ts()
        self.thread = threading.Thread(target=self._start)
        self.thread.start()
        return self.event

    async def async_wait(self):
        await self.event.wait()
        self.event.clear()
        return self.get_event_message()

    def _start(self):
        self.player = MPVPlayer(**self.mpv_kwargs)
        self.player.set_on_play(self.on_play)
        self.player.set_on_pause(self.on_pause)
        self.player.set_on_quit(self.on_quit)
        if self.file_path:
            self.player.play_path(str(self.file_path))

    def get_event_message(self):
        return self.event_message

    def send_message(self, cmd, payload):
        # TODO: Not thread safe enough
        self.event_message[0] = cmd
        self.event_message[1] = payload
        self.event.set()

    def on_play(self, name, value):
        now = now_utc_timestamp()
        ptime = self.player.get_time() or 0.0
        self.log(f"Play event")
        self.send_message("syncplay", f"{now} {ptime}")

    def on_pause(self, name, value):
        now = now_utc_timestamp()
        ptime = self.player.get_time() or 0.0
        self.log(f"Pause event")
        self.send_message("syncpause", f"{now} {ptime}")

    def on_quit(self, name, value):
        print("🎉🎉🎉 QUIT 🎉🎉🎉")

    def calculate_from_timestamp_sync(self, timestamp, seektime) -> float:
        """
        timestamp: seconds (datetime.now(UTC).timestamp())
        seektime: seconds of video playing
        Returns calculated seek position with time difference offset
        """
        timestamp = float(timestamp or 0)
        seektime = float(seektime or 0)
        now = now_utc_timestamp()
        diff = now - timestamp
        if diff > 60.0:
            raise ValueError(
                f"Clients delay is too big, clock is out of sync or network"
                f" error (diff: {diff})")
        return seektime + diff

    def cmd_setfile(self, data):
        self.player.play_path(data)

    def cmd_play(self, data):
        self.player.play()

    def cmd_pause(self, data):
        self.player.pause()
        print("TIME:", self.player.get_time())

    def cmd_seek(self, data):
        self.player.seek_absolute(data)

    def cmd_message(self, data):
        self.player.show_message(data or None)

    def cmd_mpv_command(self, data):
        self.player.command(data.split(' '))

    def cmd_syncplay(self, data):
        """
        Seek to the reportet seektime, adding the timestamp delay and then play
        """
        target = self.calculate_from_timestamp_sync(*data.split(' ', 1))
        self.player.seek_absolute(target)
        self.player.play()

    def cmd_syncpause(self, data):
        """
        Pause and then seek to the reportet seektime, adding the timestamp delay
        """
        seektime = data.rsplit(' ', 1)[-1]
        self.player.seek_absolute(seektime)
        self.player.pause()

    def cmd_peernew(self, data):
        self.show_message_during(f"[#{data}] New peer")

    def cmd_peerquit(self, data):
        self.show_message_during(f"[#{data}] Peer quit")

    def show_message_during(self, message, seconds=5):
        player = self.player

        def clear_message():
            player.show_message("")

        player.show_message(message)
        self.message_timer.fire(clear_message, seconds)

    def kill(self):
        if self.player:
            del self.player
        self.player = None
