import asyncio
from pathlib import Path
from watchfiles import awatch, Change
from .broadcaster import Broadcaster


class FileWatcher:
    def __init__(
        self,
        paths: list[str | Path],
        broadcaster: Broadcaster,
        build_command: list[str] | None = None,
        exclude_paths: list[str | Path] | None = None,
    ) -> None:
        self._paths = paths
        self._broadcaster = broadcaster
        self._build_command = build_command
        self._exclude = [Path(p).resolve() for p in (exclude_paths or [])]
        self._stop_event = asyncio.Event()
        self._task: asyncio.Task | None = None

    def _watch_filter(self, change: Change, path: str) -> bool:
        resolved = Path(path).resolve()
        return not any(
            resolved == exc or exc in resolved.parents for exc in self._exclude
        )

    def start(self) -> None:
        self._stop_event.clear()
        self._task = asyncio.get_event_loop().create_task(self._run())

    async def stop(self) -> None:
        self._stop_event.set()
        if self._task is not None:
            await self._task
            self._task = None

    async def _run_build(self) -> None:
        if not self._build_command:
            return
        proc = await asyncio.create_subprocess_exec(*self._build_command)
        await proc.wait()

    async def _run(self) -> None:
        await self._run_build()
        async for _ in awatch(
            *self._paths,
            watch_filter=self._watch_filter,
            stop_event=self._stop_event,
        ):
            await self._run_build()
            await self._broadcaster.broadcast("reload")
