import json
from pathlib import Path
from typing import Any
from typing import Protocol

from pydantic import BaseModel

from ssb_pubmd.adapters.cms_client.models import CmsResponse
from ssb_pubmd.interface.component.models import UserData


class StorageError(Exception): ...


class Storage(Protocol):
    def update(self, key: str, data: UserData | CmsResponse) -> None: ...
    def get_user_data(self, key: str) -> UserData: ...
    def get_cms_data(self, key: str) -> CmsResponse | None: ...


class Content(BaseModel):
    user_data: UserData | None = None
    cms_data: CmsResponse | None = None


class LocalFileStorage:
    path: Path

    def __init__(self, project_folder: Path) -> None:
        self.path = project_folder / ".ssbno.json"
        if not self.path.exists():
            with self.path.open("w") as f:
                json.dump({}, f)

    def _load(self) -> dict[str, dict[str, Any]]:
        with self.path.open() as f:
            return json.load(f)  # type: ignore

    def _save(self, data: dict[str, dict[str, Any]]) -> None:
        with self.path.open("w") as f:
            json.dump(data, f, indent=2)

    def update(self, key: str, data: UserData | CmsResponse) -> None:
        store = self._load()

        if isinstance(data, UserData):
            new = Content(user_data=data)
        else:
            new = Content(cms_data=data)

        new_data = new.model_dump(exclude_none=True)

        current = store.get(key, {})
        current.update(new_data)
        store[key] = current

        self._save(store)

    def get_user_data(self, key: str) -> UserData:
        store = self._load()
        data = store.get(key, {})

        try:
            content = Content(**data)
            assert isinstance(content.user_data, UserData)
            return content.user_data
        except Exception as err:
            raise StorageError(
                f"Content with key '{key}' has not been created yet."
            ) from err

    def get_cms_data(self, key: str) -> CmsResponse | None:
        store = self._load()
        data = store.get(key, {})

        content = Content(**data)
        return content.cms_data
