import os
from dataclasses import dataclass
from typing import Any
from typing import NamedTuple
from typing import Protocol

import requests
from dapla_auth_client import AuthClient

from ssb_pubmd.adapters.content_parser import Content
from ssb_pubmd.config import Config


class PublishClientError(Exception): ...

class HttpClient(Protocol):
    def post(
        self, url: str, headers: dict[str, str], payload: dict[str, Any]
    ) -> dict[str, str]: ...

class RequestsHttpClient:
    def post(
        self, url: str, headers: dict[str, str], payload: dict[str, Any]
    ) -> dict[str, str]:
        try:
            response = requests.post(
                url,
                headers=headers,
                json=payload,
            )
            body = response.json()
        except Exception as err:
            raise PublishClientError("Sync failed. Could not parse response.") from err
        if not response.ok:
            raise PublishClientError(
                f"Sync failed. Response message: {body.get('msg', 'no message')}"
            )
        return body  # type: ignore

class TokenClient(Protocol):
    def get_token(self) -> str: ...


class LocalTokenClient:
    def get_token(self) -> str:
        return os.environ.get("OIDC_TOKEN", "")


class DaplaTokenClient:
    def get_token(self) -> str:
        return AuthClient.fetch_personal_token(audiences=["ssbno"])

class Response(NamedTuple):
    publish_path: str
    publish_id: str
    publish_url: str
    publish_html: str

class PublishClient(Protocol):
    http_client: HttpClient

    def send_content(self, content: Content) -> Response: ...

DEFAULT_HTTP_CLIENT = RequestsHttpClient()
DEFULT_TOKEN_CLIENT = LocalTokenClient()
@dataclass
class MimirPublishClient:
    base_url: str
    endpoint: str
    preview_base_path: str
    http_client: HttpClient = DEFAULT_HTTP_CLIENT
    token_client: TokenClient = DEFULT_TOKEN_CLIENT

    def _create_headers(self) -> dict[str, str]:
        return {
            "Authorization": f"Bearer {self.token_client.get_token()}",
            "Content-Type": "application/json",
        }

    def send_content(self, content: Content) -> Response:
        headers = self._create_headers()
        response_body = self.http_client.post(
            url=f"{self.base_url}{self.endpoint}",
            headers=headers,
            payload=content.serialize(),
        )

        id_ = response_body.get("_id")
        path = response_body.get("_path")

        if path is None or id_ is None:
            raise PublishClientError("Sync failed. Could not parse response body.")

        macro_type = (
            content.content_type
            if content.content_type in ["highchart", "factBox"]
            else None
        )
        if id_ is not None and macro_type is not None:
            html = f"<p>[ {macro_type} {content.content_type}=&quot;{id_}&quot; /]</p>"
        else:
            html = ""

        return Response(
            publish_path=path,
            publish_id=id_,
            publish_url=self.base_url + self.preview_base_path + path,
            publish_html=html,
        )


def get_publish_client(config: Config) -> PublishClient:
    return MimirPublishClient(
        base_url=config.publish_base_url,
        endpoint="/webapp/pubmd",
        preview_base_path=config.publish_admin_path + "/site/preview/default/draft",
        token_client=DaplaTokenClient()
        if config.use_dapla_token_client
        else DEFULT_TOKEN_CLIENT,
    )