import re
from dataclasses import dataclass
from typing import Literal, Optional, Union
from urllib.parse import urljoin

from smb.common.http_client import collect_errors

from smb.common.aiotvm import HttpClientWithTvm, TvmClient

__all__ = [
    "AvatarsClient",
    "AvatarsInstallation",
    "PutImageResult",
    "avatars_installations",
]


@dataclass(frozen=True)
class AvatarsInstallation:
    outer_read_url: str
    inner_read_url: str
    write_url: str


avatars_installations = {
    "testing": AvatarsInstallation(
        outer_read_url="https://avatars.mdst.yandex.net",
        inner_read_url="https://avatars-int.mdst.yandex.net",
        write_url="http://avatars-int.mdst.yandex.net:13000",
    ),
    "production": AvatarsInstallation(
        outer_read_url="https://avatars.mds.yandex.net",
        inner_read_url="https://avatars-int.mds.yandex.net",
        write_url="http://avatars-int.mds.yandex.net:13000",
    ),
}


@dataclass(frozen=True)
class PutImageResult:
    __slots__ = ("root_url", "response")

    _orig_postfix_re = re.compile("orig$")

    root_url: str
    response: dict

    @property
    def url_template(self) -> str:
        return urljoin(
            self.root_url,
            self._orig_postfix_re.sub("%s", self.response["sizes"]["orig"]["path"]),
        )

    @property
    def orig_url(self) -> str:
        return urljoin(self.root_url, self.response["sizes"]["orig"]["path"])


class AvatarsClient(HttpClientWithTvm):
    __slots__ = ("installation", "namespace", "inner")

    installation: AvatarsInstallation
    namespace: str
    inner: bool

    def __init__(
        self,
        installation: Literal["testing", "production"],
        namespace: str,
        tvm_client: TvmClient,
        tvm_destination: Union[str, int],
        tvm_source: Optional[Union[str, int]] = None,
        inner: bool = False,
    ):
        super().__init__(
            url=avatars_installations[installation].write_url,
            tvm=tvm_client,
            tvm_source=tvm_source,
            tvm_destination=tvm_destination,
        )
        self.installation = avatars_installations[installation]
        self.namespace = namespace
        self.inner = inner

    @collect_errors
    async def put_image_by_url(
        self,
        image_url: str,
        image_name: Optional[str] = None,
        timeout: Optional[int] = None,
    ) -> PutImageResult:
        timeout = dict(timeout=timeout) if timeout else dict()

        put_uri = f"/put-{self.namespace}"
        metric_name = "/put-{namespace}"
        if image_name:
            put_uri += f"/{image_name}"
            metric_name += "/{image_name}"

        response = await self.request(
            method="GET",
            uri=put_uri,
            expected_statuses=[200],
            params={"url": image_url},
            metric_name=metric_name,
            **timeout,
        )

        return PutImageResult(root_url=self.read_url, response=response)

    @property
    def write_url(self) -> str:
        return self.installation.write_url

    @property
    def read_url(self) -> str:
        return (
            self.installation.inner_read_url
            if self.inner
            else self.installation.outer_read_url
        )
