import pathlib
import typing
from abc import ABC, abstractmethod
from functools import cached_property

import msgspec
from envparse import env
from fntypes.result import Result

from telegrinder.api.error import APIError
from telegrinder.client import ABCClient

from .error import InvalidTokenError


class Token(str):
    def __new__(cls, token: str) -> typing.Self:
        if token.count(":") != 1 or not token.split(":")[0].isdigit():
            raise InvalidTokenError("Invalid token, it should look like this '123:ABC'.")
        return super().__new__(cls, token)

    def __repr__(self) -> str:
        return f"<Token: {self.bot_id}:{''.join(self.split(':')[-1])[:6]}...>"

    @classmethod
    def from_env(
        cls,
        var_name: str = "BOT_TOKEN",
        *,
        is_read: bool = False,
        path_to_envfile: str | pathlib.Path | None = None,
    ) -> typing.Self:
        if not is_read:
            env.read_envfile(path_to_envfile)
        return cls(env.str(var_name))

    @cached_property
    def bot_id(self) -> int:
        return int(self.split(":")[0])


class ABCAPI(ABC):
    http: ABCClient

    @abstractmethod
    async def request(
        self,
        method: str,
        data: dict[str, typing.Any] | None = None,
        files: dict[str, tuple[str, bytes]] | None = None,
    ) -> Result[list[typing.Any] | dict[str, typing.Any] | bool, APIError]:
        pass

    @abstractmethod
    async def request_raw(
        self,
        method: str,
        data: dict[str, typing.Any] | None = None,
        files: dict[str, tuple[str, bytes]] | None = None,
    ) -> Result[msgspec.Raw, APIError]:
        pass

    @property
    @abstractmethod
    def request_url(self) -> str:
        pass

    @property
    @abstractmethod
    def request_file_url(self) -> str:
        pass

    @property
    @abstractmethod
    def id(self) -> int:
        pass


__all__ = ("ABCAPI", "Token")
