# retoor <retoor@molodetz.nl>

from __future__ import annotations

from devplacepy.services.containers.backend.base import (
    Backend,
    BuildResult,
    ExecResult,
    PsRow,
    RunSpec,
    StatsSample,
)


class FakeBackend(Backend):
    def __init__(self) -> None:
        self.containers: dict = {}
        self.built_images: list = []
        self.removed: list = []
        self.removed_images: list = []
        self.pruned = 0
        self._counter = 0
        self.fail_build = False

    async def build(
        self, *, context_dir, dockerfile_text, tags, on_log=None, network=""
    ) -> BuildResult:
        if on_log is not None:
            await on_log(f"FAKE build {tags} net={network}")
        if self.fail_build:
            return BuildResult(success=False, error="fake build failure")
        self.built_images.append(list(tags))
        return BuildResult(
            success=True, image_id=f"sha256:fake{len(self.built_images)}"
        )

    async def run(self, spec: RunSpec) -> str:
        self._counter += 1
        cid = f"fake{self._counter:012d}"
        self.containers[cid] = {
            "name": spec.name,
            "state": "running",
            "labels": dict(spec.labels),
            "exit_code": None,
            "image": spec.image,
            "spec": spec,
            "NetworkSettings": {
                "Networks": {
                    "bridge": {
                        "IPAddress": f"10.88.0.{self._counter}",
                        "Gateway": "10.88.0.1",
                    }
                }
            },
        }
        return cid

    def _set_state(self, cid: str, state: str, exit_code=None) -> None:
        if cid in self.containers:
            self.containers[cid]["state"] = state
            if exit_code is not None:
                self.containers[cid]["exit_code"] = exit_code

    async def start(self, cid: str) -> None:
        self._set_state(cid, "running")

    async def stop(self, cid: str, timeout: int = 10) -> None:
        self._set_state(cid, "exited", exit_code=0)

    async def restart(self, cid: str) -> None:
        self._set_state(cid, "running")

    async def pause(self, cid: str) -> None:
        self._set_state(cid, "paused")

    async def unpause(self, cid: str) -> None:
        self._set_state(cid, "running")

    async def rm(self, cid: str, force: bool = False) -> None:
        self.removed.append(cid)
        self.containers.pop(cid, None)

    async def exec(self, cid, cmd, *, tty=False, on_log=None) -> ExecResult:
        line = f"FAKE exec {' '.join(cmd)}"
        if on_log is not None:
            await on_log(line)
        return ExecResult(exit_code=0, output=line)

    async def logs(self, cid, *, follow=False, tail=200, on_log=None) -> None:
        if on_log is not None:
            await on_log(f"FAKE logs for {cid}")

    async def stats_once(self, cids: list) -> dict:
        return {
            self.containers[cid]["name"]: StatsSample(cpu_pct=1.0, mem_bytes=1024)
            for cid in cids
            if cid in self.containers
        }

    async def ps(self, *, label_filter: str = "devplace.instance") -> list:
        key, _, value = label_filter.partition("=")
        rows = []
        for cid, data in self.containers.items():
            labels = data["labels"]
            if key not in labels:
                continue
            if value and labels.get(key) != value:
                continue
            rows.append(
                PsRow(
                    container_id=cid,
                    name=data["name"],
                    state=data["state"],
                    status=data["state"],
                    exit_code=data["exit_code"],
                    labels=labels,
                )
            )
        return rows

    async def inspect(self, cid: str) -> dict:
        return self.containers.get(cid, {})

    async def image_prune(self) -> None:
        self.pruned += 1

    async def remove_image(self, ref: str, force: bool = False) -> None:
        self.removed_images.append(ref)

    async def image_exists(self, ref: str) -> bool:
        return True
