from __future__ import annotations

import json
from copy import deepcopy
from pathlib import Path
from typing import Any, Mapping

from .defaults import make_default_state


def _deep_merge(target: dict[str, Any], patch: Mapping[str, Any]) -> dict[str, Any]:
    for key, value in patch.items():
        if isinstance(value, Mapping) and isinstance(target.get(key), dict):
            _deep_merge(target[key], value)
        else:
            target[key] = deepcopy(value)
    return target


class MockDataStore:
    def __init__(self, profile: Mapping[str, Any]) -> None:
        self.profile = dict(profile)
        self._defaults = make_default_state(profile)
        self._state = deepcopy(self._defaults)

    @property
    def state(self) -> dict[str, Any]:
        return self._state

    def snapshot(self) -> dict[str, Any]:
        return deepcopy(self._state)

    def reset(self) -> dict[str, Any]:
        self._state = deepcopy(self._defaults)
        return self.snapshot()

    def merge_patch(self, patch: Mapping[str, Any]) -> dict[str, Any]:
        _deep_merge(self._state, patch)
        return self.snapshot()

    def load_patch_file(self, path: str | Path) -> dict[str, Any]:
        patch = json.loads(Path(path).read_text(encoding="utf-8"))
        return self.merge_patch(patch)

    def consume_forced_error(self, method: str) -> tuple[int, str] | None:
        entry = self._state.get("forced_errors", {}).get(method)
        if not entry:
            return None
        if isinstance(entry, int):
            return entry, f"forced error for {method}"
        rc = int(entry.get("rc", 0))
        count = int(entry.get("count", 1))
        message = str(entry.get("message", f"forced error for {method}"))
        if count <= 1:
            self._state["forced_errors"].pop(method, None)
        else:
            entry["count"] = count - 1
        return rc, message

