from pathlib import Path

from controllers.types import Environments
from engine.journal_calls import pieces
from engine.reach import Reach
from engine.worktree import checkout
from features.helper_worktrees.controller import Worktrees
from features.parts import AgentContext, ToolInterceptor
from engine.gates import Runs
from providers.catalogue import workspace_folders
from engine.runtime import DEFAULT_ENV
from resources.base import SYSTEM


class TellDrift(ToolInterceptor):
    reach = Reach.BOTH
    runs = Runs.ASYNC

    def intercept(self, context: AgentContext, call) -> str:
        hook = context.hook
        subagent = context.provider.is_subagent(hook)
        here = Path(hook.cwd) if hook.cwd else Path.cwd()
        places = tuple((here / given).resolve() for given in (str(here), *(call.paths if subagent else ())))
        drifted = Worktrees(context.record, actor=SYSTEM)._drifted(places, tuple(call.commands) if subagent else ())
        if drifted is None:
            return ""
        row, found = drifted
        context.agent.whisper("drifted", working=row.working, commits=found.commits, path=row.path)
        return ""


class StayInYourCheckout(ToolInterceptor):
    reach = Reach.MAIN
    runs = Runs.SYNC

    def intercept(self, context: AgentContext, call) -> str:
        hook = context.hook
        place = Environments(context.record, actor=SYSTEM).rows.by_title(context.record.env)
        home = Path(place.folder) if place and place.folder else self.own_worktree(context.record) or context.record.root.parent
        if not hook.cwd or self.checkout_of(Path(hook.cwd)) == self.checkout_of(home) or self.goes_home(call.shell_command, home):
            return ""
        return context.feature.line("strayed", {"folder": hook.cwd, "home": home})[0]

    @staticmethod
    def own_worktree(record) -> Path | None:
        """The worktree an environment is named for, when its row does not say where it works, as a ticket's does not: the agent in it is at home there."""
        folder = record.root.parent.joinpath(*workspace_folders().worktree_home, record.env)
        return folder if record.env != DEFAULT_ENV and folder.is_dir() else None

    def checkout_of(self, folder: Path) -> Path | None:
        return checkout(folder.resolve(), workspace_folders())

    def goes_home(self, shell: str, home: Path) -> bool:
        first = pieces(shell)[0] if shell else ()
        return first[:1] == ("cd",) and len(first) == 2 and Path(first[1]).expanduser().resolve() == home.resolve()
