from dataclasses import asdict
from dataclasses import dataclass

from pydantic import TypeAdapter

from ssb_pubmd.adapters.cms_client import CmsClient
from ssb_pubmd.adapters.cms_client.models import CmsRequest
from ssb_pubmd.adapters.cms_client.models import CmsResponse
from ssb_pubmd.adapters.storage import Storage
from ssb_pubmd.adapters.storage import StorageError
from ssb_pubmd.core.document_processor import Document
from ssb_pubmd.models import Article
from ssb_pubmd.models import UserData

ARTICLE_KEY = "article"


@dataclass
class DocumentPublisher:
    response_storage: Storage
    cms_client: CmsClient

    def sync_document(self, document: Document) -> str:
        article_metadata = document.extract_metadata(target_key="ssb")
        user_data = Article(**article_metadata)

        cms_request = CmsRequest(
            target_folder=article_metadata["path"],
            user_data=user_data,
        )
        cms_response = self._ensure_article_exists(cms_request)

        self._sync_document_elements(document, cms_response.content_path)

        cms_request.content_id = cms_response.content_id
        cms_request.html = document.extract_html()
        cms_response = self.cms_client.send(cms_request)
        self.response_storage.update(ARTICLE_KEY, asdict(cms_response))

        return cms_response.preview_url

    def _ensure_article_exists(self, cms_request: CmsRequest) -> CmsResponse:
        stored_cms_response = self._get_stored_cms_response(ARTICLE_KEY)
        if stored_cms_response is not None:
            return stored_cms_response
        cms_response = self.cms_client.send(cms_request)
        self.response_storage.update(ARTICLE_KEY, asdict(cms_response))
        return cms_response

    def _sync_document_elements(
        self, document: Document, article_publish_path: str
    ) -> None:
        elements = document.extract_elements(target_class="ssb")
        for element in elements:
            user_data: UserData = TypeAdapter(UserData).validate_python(
                element.metadata
            )
            stored_response_data = self._get_stored_cms_response(element.id)

            cms_request = CmsRequest(
                target_folder=article_publish_path,
                user_data=user_data,
                html=element.inner_html,
            )
            if stored_response_data is not None:
                cms_request.content_id = stored_response_data.content_id
            cms_response = self.cms_client.send(cms_request)

            self.response_storage.update(key=element.id, data=asdict(cms_response))
            document.replace_element(element.id, new_html=cms_response.html_snippet)

    def _get_stored_cms_response(self, key: str) -> CmsResponse | None:
        try:
            data = self.response_storage.get(key)
            return CmsResponse(**data)
        except StorageError:
            return None
