import asyncio

from psutil import Process

from ribosome.record import list_field, bool_field, field
from ribosome.machine.transition import may_handle, handle, Transitions
from ribosome.machine.message_base import message
from ribosome.machine.state import StateMachine
from ribosome.machine.base import UnitIO

from amino import Empty, Try, __, List, L, _
from amino.io import IO

from myo.record import Record
from myo.components.tmux.message import WatchCommand
from myo.command import CommandJob
from myo.ui.tmux.pane import Pane


class WatcherState(Record):
    commands = list_field()
    watching = bool_field()

    def remove(self, cmds):
        return self.set(commands=self.commands.remove_all(cmds))


class WatchedCommand(Record):
    command = field(CommandJob)
    pane = field(Pane)


Start = message('Start')
Stop = message('Stop')
Tick = message('Tick')
Tail = message('Tail', 'pane')
Terminated = message('Terminated', 'command', 'pane')


class WatcherTransitions(Transitions):

    @property
    async def _sleep(self):
        await asyncio.sleep(self.machine.interval)
        return Empty()

    @may_handle(WatchCommand)
    def watch_command(self):
        pane = self.msg.pane
        wc = WatchedCommand(command=self.msg.command, pane=pane)
        return self.data.append1.commands(wc), Tail(pane), Start()

    @handle(Start)
    def start_rec(self):
        return self.data.watching.no.maybe_call(
            lambda: (self.data.set(watching=True), Tick().pub))

    @may_handle(Stop)
    def stop_rec(self):
        return self.data.set(watching=False)

    @handle(Tick)
    def tick(self):
        return self.data.watching.maybe_call(self._tick)

    @may_handle(Tail)
    def tail(self):
        return

    def _tick(self):
        restart = List(self._sleep, Tick().pub)
        done = self.data.commands.flat_map(self._check_proc)
        data = self.data.remove(done)
        term_msg = lambda wc: Terminated(wc.command, wc.pane)
        term = (done / term_msg / L(IO.call)(self.machine.bubble, _) /
                UnitIO)
        return term.cons(data) + restart

    def _check_proc(self, wc):
        proc = wc.pane.pid // (lambda a: Try(Process, a).to_maybe)
        return proc.exists(__.is_running()).no.maybe(wc)


class Watcher(StateMachine):
    _data_type = WatcherState

    Transitions = WatcherTransitions

    def __init__(self, machine, interval: float) -> None:
        super().__init__(List(), parent=machine)
        self.machine = machine
        self.interval = interval

    @property
    def title(self):
        return 'tmux watcher'

    @property
    def init(self):
        return WatcherState()

__all__ = ('Watcher',)
