import aiohttp
from urllib.parse import urlencode

from typing import List, Literal

from . import models
from . import errors


class BaseAPI:

    def __init__(self, session: aiohttp.ClientSession, base_url: str):
        self.session = session
        self.BASE_URL = base_url

    async def _handle_response(self, resp: aiohttp.ClientResponse):

        match resp.status:
            case 200 | 201:
                return await resp.json()
            case 400:
                raise errors.RequestValidationError(
                    "Request parameters validation error (400)"
                )
            case 404:
                raise errors.DataNotFoundError(
                    "Requested data not found in database (404)"
                )
            case 429:
                raise errors.TooManyRequests("Too Many Requests (429)")
            case _:
                try:
                    error_text = await resp.text()
                except:
                    error_text = ""
                raise errors.UnexpectedResponseError(resp.status, error_text)

    async def get(self, path: str, params: dict = None):
        url = f"{self.BASE_URL}/{path.lstrip('/')}"
        if params:
            query = urlencode(
                {
                    k: ",".join(v) if isinstance(v, list) else v
                    for k, v in params.items()
                }
            )
            url += f"?{query}"
        async with self.session.get(url) as resp:
            return await self._handle_response(resp)

    async def post(self, path: str, data: dict = None, json: dict = None):
        url = f"{self.BASE_URL}/{path.lstrip('/')}"

        async with self.session.post(url, data=data, json=json) as resp:
            return await self._handle_response(resp)

    async def get_text(self, path: str, params: dict = None) -> str:
        url = f"{self.BASE_URL}/{path.lstrip('/')}"
        if params:
            query = urlencode(
                {
                    k: ",".join(v) if isinstance(v, list) else v
                    for k, v in params.items()
                }
            )
            url += f"?{query}"
        async with self.session.get(url) as resp:
            if resp.status == 200:
                return await resp.text()
            await self._handle_response(resp)

    async def get_bytes(self, path: str, params: dict = None) -> bytes:
        url = f"{self.BASE_URL}/{path.lstrip('/')}"
        if params:
            query = urlencode(
                {
                    k: ",".join(v) if isinstance(v, list) else v
                    for k, v in params.items()
                }
            )
            url += f"?{query}"
        async with self.session.get(url) as resp:
            if resp.status == 200:
                return await resp.read()
            await self._handle_response(resp)


class APIInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def version(self) -> models.APIVersion:
        data = await self.client.get("/version")
        return models.APIVersion(**data)


class TaskProcessInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def find_tasks(
        self,
        input: list[str],
        owner: str | None = None,
        branch: str | None = None,
        state: list[str] | None = None,
        tasks_limit: int = 100,
        by_package: bool = False,
    ) -> models.TasksListModel:
        params = {
            k: v
            for k, v in {
                "input": input,
                "owner": owner,
                "branch": branch,
                "state": state,
                "tasks_limit": tasks_limit,
                "by_package": by_package,
            }.items()
            if v is not None
        }
        data = await self.client.get("/task/progress/find_tasks", params)
        return models.TasksListModel(**data)

    async def task_info(self, task_id: int) -> models.TaskProgressTaskInfoModel:
        data = await self.client.get(f"/task/progress/task_info/{task_id}")
        return models.TaskProgressTaskInfoModel(**data)

    async def all_packagesets(self) -> models.AllTasksBranchesModel:
        data = await self.client.get("/task/progress/all_packagesets")
        return models.AllTasksBranchesModel(**data)

    async def all_tasks_branches(self) -> models.AllTasksBranchesModel:
        data = await self.client.get("/task/progress/all_tasks_branches")
        return models.AllTasksBranchesModel(**data)

    async def find_tasks_lookup(
        self,
        input: list[str],
        branch: str | None = None,
        tasks_limit: int = 10,
    ) -> models.FindTasksModel:
        params = {
            k: v
            for k, v in {
                "input": input,
                "branch": branch,
                "tasks_limit": tasks_limit,
            }.items()
            if v is not None
        }
        data = await self.client.get("/task/progress/find_tasks_lookup", params)
        return models.FindTasksModel(**data)

    async def last_tasks(
        self, branch: str | None = None, tasks_limit: int = 10
    ) -> models.TasksListModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "tasks_limit": tasks_limit,
            }.items()
            if v is not None
        }
        data = await self.client.get("/task/progress/last_tasks", params)
        return models.TasksListModel(**data)


class TaskInfo:
    def __init__(self, client: BaseAPI):
        self.client = client
        self.progress = TaskProcessInfo(self.client)

    async def build_dependency_set(
        self, task_id: int, arch: str = "x86_64"
    ) -> models.BuildDependencySetModel:
        data = await self.client.get(
            f"/task/build_dependency_set/{task_id}", {"arch": arch}
        )
        return models.BuildDependencySetModel(**data)

    async def check_images(self, payload: dict) -> models.CheckImagesOutputModel:
        data = await self.client.post("/task/check_images", json=payload)
        return models.CheckImagesOutputModel(**data)

    async def find_images(self, task_id: int) -> models.FindImagesByTaskModel:
        data = await self.client.get(f"/task/find_images/{task_id}")
        return models.FindImagesByTaskModel(**data)

    async def find_packageset(
        self, task_id: int, branches: list[str] | None = None
    ) -> models.TaskFindPackagesetModel:
        params = {
            k: v
            for k, v in {
                "branches": branches,
            }.items()
            if v is not None
        }
        data = await self.client.get(f"/task/find_packageset/{task_id}", params or None)
        return models.TaskFindPackagesetModel(**data)

    async def misconflict(
        self, task_id: int, archs: list[str] | None = None, no_cache: str | None = None
    ) -> models.TaskMisconflictPackagesModel:
        params = {
            k: v
            for k, v in {
                "archs": archs,
                "no_cache": no_cache,
            }.items()
            if v is not None
        }
        data = await self.client.get(f"/task/misconflict/{task_id}", params or None)
        return models.TaskMisconflictPackagesModel(**data)

    async def needs_approval(
        self,
        acl_group: Literal["maint", "tester"],
        branches: list[str] | None = None,
        before: str | None = None,
    ) -> models.NeedsApprovalModel:
        params = {
            k: v
            for k, v in {
                "acl_group": acl_group,
                "branches": branches,
                "before": before,
            }.items()
            if v is not None
        }
        data = await self.client.get("/task/needs_approval", params)
        return models.NeedsApprovalModel(**data)

    async def packages(self, task_id: int) -> models.TaskPackagesModel:
        data = await self.client.get(f"/task/packages/{task_id}")
        return models.TaskPackagesModel(**data)

    async def task_diff(self, task_id: int) -> models.TaskDiffModel:
        data = await self.client.get(f"/task/task_diff/{task_id}")
        return models.TaskDiffModel(**data)

    async def task_history(
        self,
        branch: str,
        start_task: int = 0,
        end_task: int = 0,
        start_date: str | None = None,
        end_date: str | None = None,
    ) -> models.TaskHistoryModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "start_task": start_task,
                "end_task": end_task,
                "start_date": start_date,
                "end_date": end_date,
            }.items()
            if v is not None
        }
        data = await self.client.get("/task/task_history", params)
        return models.TaskHistoryModel(**data)

    async def task_info(
        self,
        task_id: int,
        try_: int | None = None,
        iteration: int | None = None,
        no_cache: str | None = None,
        states: list[str] | None = None,
    ) -> models.TaskInfoModel:
        params = {
            k: v
            for k, v in {
                "try": try_,
                "iteration": iteration,
                "no_cache": no_cache,
                "states": states,
            }.items()
            if v is not None
        }
        data = await self.client.get(f"/task/task_info/{task_id}", params or None)
        return models.TaskInfoModel(**data)

    async def task_repo(
        self, task_id: int, include_task_packages: bool = False
    ) -> models.TaskRepoModel:
        data = await self.client.get(
            f"/task/task_repo/{task_id}",
            {"include_task_packages": include_task_packages},
        )
        return models.TaskRepoModel(**data)

    async def what_depends_src(
        self,
        task_id: int,
        depth: int = 1,
        dptype: Literal["both", "source", "binary"] = "both",
        archs: list[str] | None = None,
        leaf: str | None = None,
        finite_package: bool = False,
        filter_by_package: list[str] | None = None,
        filter_by_source: str | None = None,
        oneandhalf: bool = False,
        no_cache: str | None = None,
    ) -> models.TaskBuildDependencyModel:
        params = {
            k: v
            for k, v in {
                "depth": depth,
                "dptype": dptype,
                "archs": archs,
                "leaf": leaf,
                "finite_package": finite_package,
                "filter_by_package": filter_by_package,
                "filter_by_source": filter_by_source,
                "oneandhalf": oneandhalf,
                "no_cache": no_cache,
            }.items()
            if v is not None
        }
        data = await self.client.get(
            f"/task/what_depends_src/{task_id}", params or None
        )
        return models.TaskBuildDependencyModel(**data)


class PackageInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def package_info(
        self,
        name: str | None = None,
        version: str | None = None,
        release: str | None = None,
        arch: str | None = None,
        source: bool | None = None,
        branch: str | None = None,
        disttag: str | None = None,
        sha1: str | None = None,
        packager: str | None = None,
        packager_email: str | None = None,
        full: bool | None = None,
    ) -> models.PackageInfoModel:
        params = {
            k: v
            for k, v in {
                "name": name,
                "version": version,
                "release": release,
                "arch": arch,
                "source": source,
                "branch": branch,
                "disttag": disttag,
                "sha1": sha1,
                "packager": packager,
                "packager_email": packager_email,
                "full": full,
            }.items()
            if v is not None
        }
        data = await self.client.get("/package/package_info", params)
        return models.PackageInfoModel(**data)

    async def packages_by_file_names(
        self, files: List[str], branch: str, arch: str | None = None
    ) -> models.PackageByFileNameModel:
        payload = models.PackagesByFileNamesJsonModel(
            files=files, branch=branch, arch=arch
        )
        data = await self.client.post(
            "/package/packages_by_file_names",
            json=payload.model_dump(exclude_none=True),
        )
        return models.PackageByFileNameModel(**data)

    async def build_dependency_set(
        self,
        branch: str,
        packages: list[str],
        arch: str = "x86_64",
    ) -> models.BuildDependencySetModel:
        data = await self.client.get(
            "/package/build_dependency_set",
            {"branch": branch, "packages": packages, "arch": arch},
        )
        return models.BuildDependencySetModel(**data)

    async def find_packageset(
        self, packages: list[str], branches: list[str] | None = None
    ) -> models.PackageFindPackagesetModel:
        params = {
            k: v
            for k, v in {
                "packages": packages,
                "branches": branches,
            }.items()
            if v is not None
        }
        data = await self.client.get("/package/find_packageset", params)
        return models.PackageFindPackagesetModel(**data)

    async def maintainer_score(
        self, branch: str, name: str
    ) -> models.MaintainerScoreModel:
        data = await self.client.get(
            "/package/maintainer_score", {"branch": branch, "name": name}
        )
        return models.MaintainerScoreModel(**data)

    async def misconflict(
        self,
        packages: list[str],
        branch: str,
        archs: list[str] | None = None,
    ) -> models.PackageMisconflictPackagesModel:
        params = {
            k: v
            for k, v in {
                "packages": packages,
                "branch": branch,
                "archs": archs,
            }.items()
            if v is not None
        }
        data = await self.client.get("/package/misconflict", params)
        return models.PackageMisconflictPackagesModel(**data)

    async def package_by_file_md5(
        self, branch: str, md5: str, arch: str = "x86_64"
    ) -> models.PackageByFileNameModel:
        data = await self.client.get(
            "/package/package_by_file_md5", {"branch": branch, "md5": md5, "arch": arch}
        )
        return models.PackageByFileNameModel(**data)

    async def package_by_file_name(
        self, file: str, branch: str, arch: str = "x86_64"
    ) -> models.PackageByFileNameModel:
        data = await self.client.get(
            "/package/package_by_file_name",
            {"file": file, "branch": branch, "arch": arch},
        )
        return models.PackageByFileNameModel(**data)

    async def package_files(self, pkghash: int) -> models.PackageFilesModel:
        data = await self.client.get(f"/package/package_files/{pkghash}")
        return models.PackageFilesModel(**data)

    async def repocop(
        self,
        branch: str,
        package_name: str,
        package_version: str | None = None,
        package_release: str | None = None,
        bin_package_arch: str | None = None,
        package_type: Literal["source", "binary"] = "source",
    ) -> models.RepocopJsonGetListModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "package_name": package_name,
                "package_version": package_version,
                "package_release": package_release,
                "bin_package_arch": bin_package_arch,
                "package_type": package_type,
            }.items()
            if v is not None
        }
        data = await self.client.get("/package/repocop", params)
        return models.RepocopJsonGetListModel(**data)

    async def repocop_post(self, packages: list[dict]) -> None:
        await self.client.post("/package/repocop", json={"packages": packages})

    async def specfile_by_hash(self, pkghash: int) -> models.PackageSpecfileModel:
        data = await self.client.get(f"/package/specfile_by_hash/{pkghash}")
        return models.PackageSpecfileModel(**data)

    async def specfile_by_name(
        self, branch: str, name: str
    ) -> models.PackageSpecfileModel:
        data = await self.client.get(
            "/package/specfile_by_name", {"branch": branch, "name": name}
        )
        return models.PackageSpecfileModel(**data)

    async def unpackaged_dirs(
        self, branch: str, packager: str, archs: list[str] | None = None
    ) -> models.UnpackagedDirsModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "packager": packager,
                "archs": archs,
            }.items()
            if v is not None
        }
        data = await self.client.get("/package/unpackaged_dirs", params)
        return models.UnpackagedDirsModel(**data)

    async def what_depends_src(
        self,
        packages: list[str],
        branch: str,
        depth: int = 1,
        dptype: Literal["both", "source", "binary"] = "both",
        archs: list[str] | None = None,
        leaf: str | None = None,
        finite_package: bool = False,
        filter_by_package: list[str] | None = None,
        filter_by_source: str | None = None,
        oneandhalf: bool = False,
        use_last_tasks: bool = False,
    ) -> models.PackageBuildDependencyModel:
        params = {
            k: v
            for k, v in {
                "packages": packages,
                "branch": branch,
                "depth": depth,
                "dptype": dptype,
                "archs": archs,
                "leaf": leaf,
                "finite_package": finite_package,
                "filter_by_package": filter_by_package,
                "filter_by_source": filter_by_source,
                "oneandhalf": oneandhalf,
                "use_last_tasks": use_last_tasks,
            }.items()
            if v is not None
        }
        data = await self.client.get("/package/what_depends_src", params)
        return models.PackageBuildDependencyModel(**data)


class PackagesetInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def active_packagesets(self) -> models.PackageSetActivePackageSetsModel:
        data = await self.client.get("/packageset/active_packagesets")
        return models.PackageSetActivePackageSetsModel(**data)

    async def repository_statistics(
        self, branch: str | None = None
    ) -> models.RepositoryStatisticsModel:
        if branch:
            data = await self.client.get(
                "/packageset/repository_statistics", {"branch": branch}
            )
        else:
            data = await self.client.get("/packageset")
        return models.RepositoryStatisticsModel(**data)

    async def compare_packagesets(
        self, pkgset1: str, pkgset2: str
    ) -> models.PackagesetCompareModel:
        data = await self.client.get(
            "/packageset/compare_packagesets", {"pkgset1": pkgset1, "pkgset2": pkgset2}
        )
        return models.PackagesetCompareModel(**data)

    async def maintainer_scores(self, branch: str) -> models.MaintainerScoresBatchModel:
        data = await self.client.get(
            "/packageset/maintainer_scores", {"branch": branch}
        )
        return models.MaintainerScoresBatchModel(**data)

    async def packages_by_component(
        self, branch: str, arch: str, component: str
    ) -> models.PackagesByUuidModel:
        data = await self.client.get(
            "/packageset/packages_by_component",
            {"branch": branch, "arch": arch, "component": component},
        )
        return models.PackagesByUuidModel(**data)

    async def packages_by_uuid(self, uuid: str) -> models.PackagesByUuidModel:
        data = await self.client.get("/packageset/packages_by_uuid", {"uuid": uuid})
        return models.PackagesByUuidModel(**data)

    async def pkgset_status_get(self) -> models.PackageSetStatusGetModel:
        data = await self.client.get("/packageset/pkgset_status")
        return models.PackageSetStatusGetModel(**data)

    async def pkgset_status_post(self, payload: dict) -> None:
        await self.client.post("/packageset/pkgset_status", json=payload)

    async def repository_packages(
        self,
        branch: str,
        package_type: Literal["all", "source", "binary"] = "all",
        archs: list[str] | None = None,
        include_done_tasks: bool = False,
    ) -> models.PackagesetPackagesModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "package_type": package_type,
                "archs": archs,
                "include_done_tasks": include_done_tasks,
            }.items()
            if v is not None
        }
        data = await self.client.get("/packageset/repository_packages", params)
        return models.PackagesetPackagesModel(**data)


class AclInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def groups(self, branch: str, name: str | None = None):
        data = {"branch": branch}
        if name:
            data["name"] = name

        data = await self.client.get("/acl/groups", data)
        return models.AclGroupsModel(**data)

    async def by_packages(
        self, branch: str, packages_names: list[str]
    ) -> models.AclByPackagesModel:
        params = {
            "branch": branch,
            "packages_names": packages_names,
        }
        data = await self.client.get("/acl/by_packages", params)
        return models.AclByPackagesModel(**data)

    async def maintainer_groups(
        self, nickname: str, branch: list[str] | None = None
    ) -> models.AclMaintainerGroupsModel:
        params = {
            k: v
            for k, v in {
                "nickname": nickname,
                "branch": branch,
            }.items()
            if v is not None
        }
        data = await self.client.get("/acl/maintainer_groups", params)
        return models.AclMaintainerGroupsModel(**data)


class BugInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def bugzilla_by_maintainer(
        self, maintainer_nickname: str, by_acl: str | None = None
    ) -> models.BugzillaInfoModel | None:
        try:
            data = await self.client.get(
                "/bug/bugzilla_by_maintainer",
                {
                    "maintainer_nickname": maintainer_nickname,
                    "by_acl": by_acl if by_acl else "none",
                },
            )
        except errors.DataNotFoundError:
            return None
        return models.BugzillaInfoModel(**data)

    async def bugzilla_by_package(
        self,
        package_name: str,
        package_type: Literal["source", "binary"] = "source",
    ) -> models.BugzillaInfoModel:
        data = await self.client.get(
            "/bug/bugzilla_by_package",
            {"package_name": package_name, "package_type": package_type},
        )
        return models.BugzillaInfoModel(**data)

    async def bugzilla_by_image_edition(
        self, branch: str, edition: str
    ) -> models.BugzillaInfoModel:
        data = await self.client.get(
            "/bug/bugzilla_by_image_edition", {"branch": branch, "edition": edition}
        )
        return models.BugzillaInfoModel(**data)


class FileInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def packages_by_file(
        self, branch: str, file_name: str
    ) -> models.FilePackagesByFileModel:
        data = await self.client.get(
            "/file/packages_by_file", {"branch": branch, "file_name": file_name}
        )
        return models.FilePackagesByFileModel(**data)

    async def search(
        self, branch: str, file_name: str, limit: int | None = None
    ) -> models.FilesModel:
        data = await self.client.get(
            "/file/search",
            {
                "branch": branch,
                "file_name": file_name,
                "limit": limit if limit else 1000,
            },
        )
        return models.FilesModel(**data)

    async def fast_lookup(
        self, branch: str, file_name: str, limit: int = 10
    ) -> models.FastFileSearchModel:
        data = await self.client.get(
            "/file/fast_lookup",
            {"branch": branch, "file_name": file_name, "limit": limit},
        )
        return models.FastFileSearchModel(**data)


class SiteInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def all_maintainers(self, branch: str) -> models.AllMaintainersModel:
        return await self.all_maintainers_with_nicknames(branch)

    async def all_maintainers_with_nicknames(
        self, branch: str
    ) -> models.AllMaintainersModel:
        data = await self.client.get(
            "/site/all_maintainers_with_nicknames", {"branch": branch}
        )
        return models.AllMaintainersModel(**data)

    async def find_source_package(
        self, branch: str, name: str
    ) -> models.FindSourcePackageInBranch:
        data = await self.client.get(
            "/site/find_source_package", {"branch": branch, "name": name}
        )
        return models.FindSourcePackageInBranch(**data)

    async def maintainer_info(
        self, branch: str, maintainer_nickname: str
    ) -> models.MaintainerInfoModel:
        data = await self.client.get(
            "/site/maintainer_info",
            {"branch": branch, "maintainer_nickname": maintainer_nickname},
        )
        return models.MaintainerInfoModel(**data)

    async def package_info(
        self,
        branch: str,
        pkghash: int,
        changelog_last: int = 3,
        package_type: Literal["source", "binary"] = "source",
    ) -> models.SitePackageInfoModel:
        data = await self.client.get(
            f"/site/package_info/{pkghash}",
            {
                "branch": branch,
                "changelog_last": changelog_last,
                "package_type": package_type,
            },
        )
        return models.SitePackageInfoModel(**data)

    async def pkghash_by_binary_name(
        self, branch: str, name: str, arch: str
    ) -> models.SitePackagesetPackageHashModel:
        data = await self.client.get(
            "/site/pkghash_by_binary_name",
            {"branch": branch, "name": name, "arch": arch},
        )
        return models.SitePackagesetPackageHashModel(**data)

    async def pkghash_by_name(
        self, branch: str, name: str
    ) -> models.SitePackagesetPackageHashModel:
        data = await self.client.get(
            "/site/pkghash_by_name", {"branch": branch, "name": name}
        )
        return models.SitePackagesetPackageHashModel(**data)

    async def watch_by_maintainer(
        self, maintainer_nickname: str
    ) -> models.SiteWatchByMaintainerModel | None:
        try:
            data = await self.client.get(
                "/site/watch_by_maintainer",
                {"maintainer_nickname": maintainer_nickname},
            )
        except errors.DataNotFoundError:
            return None
        return models.SiteWatchByMaintainerModel(**data)

    async def all_pkgset_archs(self, branch: str) -> models.SiteAllArchsModel:
        data = await self.client.get("/site/all_pkgset_archs", {"branch": branch})
        return models.SiteAllArchsModel(**data)

    async def all_pkgset_archs_with_src_count(
        self, branch: str
    ) -> models.SiteAllArchsModel:
        data = await self.client.get(
            "/site/all_pkgset_archs_with_src_count", {"branch": branch}
        )
        return models.SiteAllArchsModel(**data)

    async def all_pkgsets(self) -> models.SiteAllPackagasetsModel:
        data = await self.client.get("/site/all_pkgsets")
        return models.SiteAllPackagasetsModel(**data)

    async def all_pkgsets_summary(self) -> models.SiteAllPackagesetsSummaryModel:
        data = await self.client.get("/site/all_pkgsets_summary")
        return models.SiteAllPackagesetsSummaryModel(**data)

    async def all_pkgsets_with_src_count(self) -> models.SiteAllPackagasetsModel:
        data = await self.client.get("/site/all_pkgsets_with_src_count")
        return models.SiteAllPackagasetsModel(**data)

    async def beehive_errors_by_maintainer(
        self,
        branch: str,
        maintainer_nickname: str,
        by_acl: str = "none",
    ) -> models.SiteBeehiveByMaintainerModel:
        data = await self.client.get(
            "/site/beehive_errors_by_maintainer",
            {
                "branch": branch,
                "maintainer_nickname": maintainer_nickname,
                "by_acl": by_acl,
            },
        )
        return models.SiteBeehiveByMaintainerModel(**data)

    async def binary_package_archs_and_versions(
        self, branch: str, name: str
    ) -> models.SitePackagesBinaryListModel:
        data = await self.client.get(
            "/site/binary_package_archs_and_versions", {"branch": branch, "name": name}
        )
        return models.SitePackagesBinaryListModel(**data)

    async def binary_package_scripts(
        self, pkghash: int
    ) -> models.SiteBinPackageScriptsModel:
        data = await self.client.get(f"/site/binary_package_scripts/{pkghash}")
        return models.SiteBinPackageScriptsModel(**data)

    async def deleted_package_info(
        self,
        branch: str,
        name: str,
        package_type: Literal["source", "binary"] = "source",
        arch: str | None = None,
    ) -> models.SiteDeletedPackageModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "name": name,
                "package_type": package_type,
                "arch": arch,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/deleted_package_info", params)
        return models.SiteDeletedPackageModel(**data)

    async def fast_packages_search_lookup(
        self, name: list[str], branch: str | None = None
    ) -> models.SiteFastPackagesSearchModel:
        params = {
            k: v
            for k, v in {
                "name": name,
                "branch": branch,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/fast_packages_search_lookup", params)
        return models.SiteFastPackagesSearchModel(**data)

    async def find_packages(
        self,
        name: list[str],
        branch: str | None = None,
        arch: list[str] | None = None,
    ) -> models.SiteFingPackagesModel:
        params = {
            k: v
            for k, v in {
                "name": name,
                "branch": branch,
                "arch": arch,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/find_packages", params)
        return models.SiteFingPackagesModel(**data)

    async def last_packages(
        self,
        branch: str,
        tasks_limit: int = 10,
        task_owner: str | None = None,
    ) -> models.SiteLastPackagesModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "tasks_limit": tasks_limit,
                "task_owner": task_owner,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/last_packages", params)
        return models.SiteLastPackagesModel(**data)

    async def last_packages_by_branch(
        self,
        branch: str,
        packages_limit: int = 10,
        packager: str | None = None,
    ) -> models.SiteLastBranchPackagesModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "packages_limit": packages_limit,
                "packager": packager,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/last_packages_by_branch", params)
        return models.SiteLastBranchPackagesModel(**data)

    async def last_packages_by_tasks(
        self,
        branch: str,
        tasks_limit: int = 10,
        task_owner: str | None = None,
    ) -> models.SiteLastPackagesModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "tasks_limit": tasks_limit,
                "task_owner": task_owner,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/last_packages_by_tasks", params)
        return models.SiteLastPackagesModel(**data)

    async def last_packages_with_cve_fixed(
        self, branch: str
    ) -> models.SiteLastPackagesWithCVEFixesModel:
        data = await self.client.get(
            "/site/last_packages_with_cve_fixed", {"branch": branch}
        )
        return models.SiteLastPackagesWithCVEFixesModel(**data)

    async def maintainer_branches(
        self, maintainer_nickname: str
    ) -> models.MaintainerBranchesModel:
        data = await self.client.get(
            "/site/maintainer_branches", {"maintainer_nickname": maintainer_nickname}
        )
        return models.MaintainerBranchesModel(**data)

    async def maintainer_packages(
        self,
        branch: str,
        maintainer_nickname: str,
        by_acl: str = "none",
    ) -> models.MaintainerPackagesModel:
        data = await self.client.get(
            "/site/maintainer_packages",
            {
                "branch": branch,
                "maintainer_nickname": maintainer_nickname,
                "by_acl": by_acl,
            },
        )
        return models.MaintainerPackagesModel(**data)

    async def package_changelog(
        self, pkghash: int, changelog_last: int = 3
    ) -> models.SiteChangelogModel:
        data = await self.client.get(
            f"/site/package_changelog/{pkghash}", {"changelog_last": changelog_last}
        )
        return models.SiteChangelogModel(**data)

    async def package_downloads(
        self, pkghash: int, branch: str
    ) -> models.SitePackagesDownloadsModel:
        data = await self.client.get(
            f"/site/package_downloads/{pkghash}", {"branch": branch}
        )
        return models.SitePackagesDownloadsModel(**data)

    async def package_downloads_bin(
        self, pkghash: int, branch: str, arch: str
    ) -> models.SitePackagesDownloadsModel:
        data = await self.client.get(
            f"/site/package_downloads_bin/{pkghash}", {"branch": branch, "arch": arch}
        )
        return models.SitePackagesDownloadsModel(**data)

    async def package_downloads_src(
        self, pkghash: int, branch: str
    ) -> models.SitePackagesDownloadsModel:
        data = await self.client.get(
            f"/site/package_downloads_src/{pkghash}", {"branch": branch}
        )
        return models.SitePackagesDownloadsModel(**data)

    async def package_info_brief(
        self, pkghash: int
    ) -> models.SiteBriefPackageInfoModel:
        data = await self.client.get(f"/site/package_info_brief/{pkghash}")
        return models.SiteBriefPackageInfoModel(**data)

    async def package_log_bin(self, pkghash: int) -> models.BinPackageLogElementModel:
        data = await self.client.get(f"/site/package_log_bin/{pkghash}")
        return models.BinPackageLogElementModel(**data)

    async def package_misconflict(
        self, pkghash: int, branch: str
    ) -> models.PackageMisconflictBySrcModel:
        data = await self.client.get(
            f"/site/package_misconflict/{pkghash}", {"branch": branch}
        )
        return models.PackageMisconflictBySrcModel(**data)

    async def package_name_from_repology(
        self, branch: str, name: str
    ) -> models.PackageNameFromRepologyModel:
        data = await self.client.get(
            "/site/package_name_from_repology", {"branch": branch, "name": name}
        )
        return models.PackageNameFromRepologyModel(**data)

    async def package_nvr_by_hash(
        self, pkghash: int, name: str | None = None
    ) -> models.PackageNVRByHashModel:
        params = {
            k: v
            for k, v in {
                "name": name,
            }.items()
            if v is not None
        }
        data = await self.client.get(
            f"/site/package_nvr_by_hash/{pkghash}", params or None
        )
        return models.PackageNVRByHashModel(**data)

    async def package_versions(
        self,
        name: str,
        package_type: Literal["source", "binary"] = "source",
        arch: str | None = None,
    ) -> models.SiteSourcePackagesVersionsModel:
        params = {
            k: v
            for k, v in {
                "name": name,
                "package_type": package_type,
                "arch": arch,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/package_versions", params)
        return models.SiteSourcePackagesVersionsModel(**data)

    async def package_versions_from_images(
        self, name: str, branch: str, edition: str, type: str
    ) -> models.SiteImagePackageVersionsModel:
        data = await self.client.get(
            "/site/package_versions_from_images",
            {"name": name, "branch": branch, "edition": edition, "type": type},
        )
        return models.SiteImagePackageVersionsModel(**data)

    async def package_versions_from_tasks(
        self, name: str, branch: str | None = None
    ) -> models.SItePackagesVersionsFromTasksModel:
        params = {
            k: v
            for k, v in {
                "name": name,
                "branch": branch,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/package_versions_from_tasks", params)
        return models.SItePackagesVersionsFromTasksModel(**data)

    async def packagesets_by_hash(
        self, pkghash: int
    ) -> models.SitePackagesetsByHashModel:
        data = await self.client.get(f"/site/packagesets_by_hash/{pkghash}")
        return models.SitePackagesetsByHashModel(**data)

    async def pkghash_by_nvr(
        self, name: str, branch: str, version: str, release: str
    ) -> models.SitePackagesetPackageHashByNameVersionRelease:
        data = await self.client.get(
            "/site/pkghash_by_nvr",
            {"name": name, "branch": branch, "version": version, "release": release},
        )
        return models.SitePackagesetPackageHashByNameVersionRelease(**data)

    async def pkgset_categories_count(
        self,
        branch: str,
        package_type: Literal["all", "source", "binary"] = "source",
    ) -> models.SitePackagesetCategoriesModel:
        data = await self.client.get(
            "/site/pkgset_categories_count",
            {"branch": branch, "package_type": package_type},
        )
        return models.SitePackagesetCategoriesModel(**data)

    async def pkgsets_summary_status(self) -> models.SitePackagesetsSummaryStatusModel:
        data = await self.client.get("/site/pkgsets_summary_status")
        return models.SitePackagesetsSummaryStatusModel(**data)

    async def repocop_by_maintainer(
        self,
        branch: str,
        maintainer_nickname: str,
        by_acl: str = "none",
    ) -> models.RepocopByMaintainerModel:
        data = await self.client.get(
            "/site/repocop_by_maintainer",
            {
                "branch": branch,
                "maintainer_nickname": maintainer_nickname,
                "by_acl": by_acl,
            },
        )
        return models.RepocopByMaintainerModel(**data)

    async def repository_packages(
        self,
        branch: str,
        package_type: Literal["all", "source", "binary"] = "source",
        group: str | None = None,
        buildtime: int = 0,
    ) -> models.SitePackagesModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "package_type": package_type,
                "group": group,
                "buildtime": buildtime,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/repository_packages", params)
        return models.SitePackagesModel(**data)

    async def source_package_versions(
        self, name: str
    ) -> models.SiteSourcePackagesVersionsModel:
        data = await self.client.get("/site/source_package_versions", {"name": name})
        return models.SiteSourcePackagesVersionsModel(**data)

    async def tasks_by_maintainer(
        self, branch: str, maintainer_nickname: str
    ) -> models.SiteTaskByNameModel:
        data = await self.client.get(
            "/site/tasks_by_maintainer",
            {"branch": branch, "maintainer_nickname": maintainer_nickname},
        )
        return models.SiteTaskByNameModel(**data)

    async def tasks_by_package(self, name: str) -> models.SiteTaskByNameModel:
        data = await self.client.get("/site/tasks_by_package", {"name": name})
        return models.SiteTaskByNameModel(**data)

    async def tasks_history(
        self, task_id: int | None = None
    ) -> models.SiteTasksHistoryModel:
        params = {
            k: v
            for k, v in {
                "task_id": task_id,
            }.items()
            if v is not None
        }
        data = await self.client.get("/site/tasks_history", params or None)
        return models.SiteTasksHistoryModel(**data)


class AuthInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def login(
        self,
        nickname: str,
        password: str,
        auth_provider: Literal["ldap", "keycloak"] = "ldap",
    ) -> models.AuthResponseModel:
        data = await self.client.post(
            "/auth/login",
            data={
                "nickname": nickname,
                "password": password,
                "auth_provider": auth_provider,
            },
        )
        return models.AuthResponseModel(**data)

    async def logout(self) -> models.AuthLogoutResponseModel:
        data = await self.client.post("/auth/logout")
        return models.AuthLogoutResponseModel(**data)

    async def refresh_token(self, access_token: str) -> models.AuthResponseModel:
        data = await self.client.post(
            "/auth/refresh-token", data={"access_token": access_token}
        )
        return models.AuthResponseModel(**data)


class DependenciesInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def backport_helper(
        self,
        from_branch: str,
        into_branch: str,
        packages_names: list[str],
        dp_type: Literal["both", "source", "binary"] = "both",
        archs: list[str] | None = None,
    ) -> models.BackportHelperModel:
        params = {
            k: v
            for k, v in {
                "from_branch": from_branch,
                "into_branch": into_branch,
                "packages_names": packages_names,
                "dp_type": dp_type,
                "archs": archs,
            }.items()
            if v is not None
        }
        data = await self.client.get("/dependencies/backport_helper", params)
        return models.BackportHelperModel(**data)

    async def binary_package_dependencies(
        self, pkghash: int
    ) -> models.DependenciesPackageDependenciesModel:
        data = await self.client.get(
            f"/dependencies/binary_package_dependencies/{pkghash}"
        )
        return models.DependenciesPackageDependenciesModel(**data)

    async def fast_lookup(
        self, branch: str, dp_name: str, limit: int = 10
    ) -> models.FastDependencySearchModel:
        data = await self.client.get(
            "/dependencies/fast_lookup",
            {"branch": branch, "dp_name": dp_name, "limit": limit},
        )
        return models.FastDependencySearchModel(**data)

    async def packages_by_dependency(
        self,
        branch: str,
        dp_name: str,
        dp_type: Literal["all", "provide", "require", "conflict", "obsolete"] = "all",
        last_state: bool = False,
    ) -> models.DependenciesPackagesModel:
        data = await self.client.get(
            "/dependencies/packages_by_dependency",
            {
                "branch": branch,
                "dp_name": dp_name,
                "dp_type": dp_type,
                "last_state": last_state,
            },
        )
        return models.DependenciesPackagesModel(**data)

    async def source_package_dependencies(
        self, pkghash: int, branch: str, depth: int = 1
    ) -> models.DependenciesPackageBuildDependenciesModel:
        data = await self.client.get(
            f"/dependencies/source_package_dependencies/{pkghash}",
            {"branch": branch, "depth": depth},
        )
        return models.DependenciesPackageBuildDependenciesModel(**data)

    async def what_depends_src(
        self,
        name: str,
        branch: str,
        dp_type: Literal["both", "source", "binary"] = "both",
    ) -> models.PackageBuildDependencyModel:
        data = await self.client.get(
            "/dependencies/what_depends_src",
            {"name": name, "branch": branch, "dp_type": dp_type},
        )
        return models.PackageBuildDependencyModel(**data)


class ErrataInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def advisory(
        self,
        branch: str | None = None,
        input: str | None = None,
        page: int | None = None,
        limit: int | None = None,
        sort: list[str] | None = None,
    ) -> models.AdvisoryErrataModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "input": input,
                "page": page,
                "limit": limit,
                "sort": sort,
            }.items()
            if v is not None
        }
        data = await self.client.get("/errata/advisory", params or None)
        return models.AdvisoryErrataModel(**data)

    async def branches_updates(
        self, errata_ids: list[str], exclude_json: bool = False
    ) -> models.ErrataBranchesUpdatesModel:
        data = await self.client.post(
            "/errata/branches_updates", json={"errata_ids": errata_ids}
        )
        return models.ErrataBranchesUpdatesModel(**data)

    async def errata_branches(self) -> models.ErrataBranchesModel:
        data = await self.client.get("/errata/errata_branches")
        return models.ErrataBranchesModel(**data)

    async def export_oval_branches(self) -> models.OvalBranchesModel:
        data = await self.client.get("/errata/export/oval/branches")
        return models.OvalBranchesModel(**data)

    async def export_oval(
        self,
        branch: str,
        package_name: str | None = None,
        one_file: bool = False,
    ) -> bytes:
        params = {
            k: v
            for k, v in {
                "package_name": package_name,
                "one_file": one_file,
            }.items()
            if v is not None
        }
        return await self.client.get_bytes(
            f"/errata/export/oval/{branch}", params or None
        )

    async def find_erratas(
        self,
        input: list[str] | None = None,
        branch: str | None = None,
        type: Literal["packages", "repository", "bug", "vuln", "exclusion"]
        | None = None,
        page: int | None = None,
        limit: int | None = None,
        state: Literal["all", "active", "discarded"] = "all",
    ) -> models.ErrataLastChangedModel:
        params = {
            k: v
            for k, v in {
                "input": input,
                "branch": branch,
                "type": type,
                "page": page,
                "limit": limit,
                "state": state,
            }.items()
            if v is not None
        }
        data = await self.client.get("/errata/find_erratas", params or None)
        return models.ErrataLastChangedModel(**data)

    async def find_image_erratas(
        self,
        uuid: str,
        branch: str,
        component: str | None = None,
        input: list[str] | None = None,
        type: Literal["packages", "repository", "bug", "vuln", "exclusion"]
        | None = None,
        page: int | None = None,
        limit: int | None = None,
        is_discarded: bool = False,
        sort: list[str] | None = None,
    ) -> models.ImageErrataModel:
        params = {
            k: v
            for k, v in {
                "uuid": uuid,
                "branch": branch,
                "component": component,
                "input": input,
                "type": type,
                "page": page,
                "limit": limit,
                "is_discarded": is_discarded,
                "sort": sort,
            }.items()
            if v is not None
        }
        data = await self.client.get("/errata/find_image_erratas", params)
        return models.ImageErrataModel(**data)

    async def ids(self) -> models.ErrataIdsListModel:
        data = await self.client.get("/errata/ids")
        return models.ErrataIdsListModel(**data)

    async def packages_updates(
        self, errata_ids: list[str], exclude_json: bool = False
    ) -> models.ErrataPackagesUpdatesModel:
        data = await self.client.post(
            "/errata/packages_updates", json={"errata_ids": errata_ids}
        )
        return models.ErrataPackagesUpdatesModel(**data)

    async def search(
        self,
        branch: str | None = None,
        name: str | None = None,
        vuln_id: str | None = None,
        errata_id: str | None = None,
    ) -> models.ErratasModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "name": name,
                "vuln_id": vuln_id,
                "errata_id": errata_id,
            }.items()
            if v is not None
        }
        data = await self.client.get("/errata/search", params or None)
        return models.ErratasModel(**data)


class ExportInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def beehive_ftbfs(
        self, branch: str, arch: str | None = None
    ) -> models.ExportBeehiveFTBFSListModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "arch": arch,
            }.items()
            if v is not None
        }
        data = await self.client.get("/export/beehive/ftbfs", params)
        return models.ExportBeehiveFTBFSListModel(**data)

    async def branch_binary_packages(
        self, branch: str, arch: str | None = None
    ) -> models.PackagesetPackagesExportModel:
        params = {
            k: v
            for k, v in {
                "arch": arch,
            }.items()
            if v is not None
        }
        data = await self.client.get(
            f"/export/branch_binary_packages/{branch}", params or None
        )
        return models.PackagesetPackagesExportModel(**data)

    async def branch_tree(self) -> models.BranchTreeModel:
        data = await self.client.get("/export/branch_tree")
        return models.BranchTreeModel(**data)

    async def repology(self, branch: str) -> models.RepologyExportModel:
        data = await self.client.get(f"/export/repology/{branch}")
        return models.RepologyExportModel(**data)

    async def sitemap_packages(self, branch: str) -> models.SitemapPackagesExportModel:
        data = await self.client.get(f"/export/sitemap_packages/{branch}")
        return models.SitemapPackagesExportModel(**data)

    async def translation_packages_po_files(
        self, branches: list[str], from_date: str | None = None
    ) -> bytes:
        params = {
            k: v
            for k, v in {
                "branches": branches,
                "from_date": from_date,
            }.items()
            if v is not None
        }
        return await self.client.get_bytes(
            "/export/translation/packages_po_files", params
        )


class ImageInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def active_images(
        self,
        branch: str | None = None,
        edition: str | None = None,
        version: str | None = None,
        release: str | None = None,
        variant: str | None = None,
        type: str | None = None,
    ) -> models.ActiveImagesModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "edition": edition,
                "version": version,
                "release": release,
                "variant": variant,
                "type": type,
            }.items()
            if v is not None
        }
        data = await self.client.get("/image/active_images", params or None)
        return models.ActiveImagesModel(**data)

    async def find_images_by_package_name(
        self,
        pkg_name: str,
        branch: str | None = None,
        edition: str | None = None,
        pkg_type: Literal["source", "binary"] = "source",
        img_show: Literal["active", "all"] = "all",
    ) -> models.FindImagesByPackageModel:
        params = {
            k: v
            for k, v in {
                "pkg_name": pkg_name,
                "branch": branch,
                "edition": edition,
                "pkg_type": pkg_type,
                "img_show": img_show,
            }.items()
            if v is not None
        }
        data = await self.client.get("/image/find_images_by_package_name", params)
        return models.FindImagesByPackageModel(**data)

    async def image_categories_count(
        self, uuid: str, component: str | None = None
    ) -> models.SiteImageCategoriesModel:
        params = {
            k: v
            for k, v in {
                "uuid": uuid,
                "component": component,
            }.items()
            if v is not None
        }
        data = await self.client.get("/image/image_categories_count", params)
        return models.SiteImageCategoriesModel(**data)

    async def image_info(
        self,
        branch: str | None = None,
        edition: str | None = None,
        version: str | None = None,
        release: str | None = None,
        variant: str | None = None,
        flavor: str | None = None,
        arch: str | None = None,
        component: str | None = None,
        platform: str | None = None,
        type: str | None = None,
    ) -> models.ImageInfoModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "edition": edition,
                "version": version,
                "release": release,
                "variant": variant,
                "flavor": flavor,
                "arch": arch,
                "component": component,
                "platform": platform,
                "type": type,
            }.items()
            if v is not None
        }
        data = await self.client.get("/image/image_info", params or None)
        return models.ImageInfoModel(**data)

    async def image_packages(
        self, uuid: str, group: str | None = None, component: str | None = None
    ) -> models.ImagePackagesModel:
        params = {
            k: v
            for k, v in {
                "uuid": uuid,
                "group": group,
                "component": component,
            }.items()
            if v is not None
        }
        data = await self.client.get("/image/image_packages", params)
        return models.ImagePackagesModel(**data)

    async def image_packageset(self) -> models.ImagePackageSetModel:
        data = await self.client.get("/image/image_packageset")
        return models.ImagePackageSetModel(**data)

    async def image_status_get(self) -> models.ImageStatusGetModel:
        data = await self.client.get("/image/image_status")
        return models.ImageStatusGetModel(**data)

    async def image_status_post(self, payload: dict) -> None:
        await self.client.post("/image/image_status", json=payload)

    async def image_tag_status_get(
        self, branch: str | None = None, edition: str | None = None
    ) -> models.ImageTagStatusGetModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "edition": edition,
            }.items()
            if v is not None
        }
        data = await self.client.get("/image/image_tag_status", params or None)
        return models.ImageTagStatusGetModel(**data)

    async def image_tag_status_post(self, payload: dict) -> None:
        await self.client.post("/image/image_tag_status", json=payload)

    async def image_uuid_by_tag(self, tag: str) -> models.ImageTagUUIDModel:
        data = await self.client.get("/image/image_uuid_by_tag", {"tag": tag})
        return models.ImageTagUUIDModel(**data)

    async def inspect_regular(
        self, payload: dict
    ) -> models.ImagePackagesInspectRegularModel:
        data = await self.client.post("/image/inspect/regular", json=payload)
        return models.ImagePackagesInspectRegularModel(**data)

    async def inspect_sp(self, payload: dict) -> models.ImagePackagesInspectSPModel:
        data = await self.client.post("/image/inspect/sp", json=payload)
        return models.ImagePackagesInspectSPModel(**data)

    async def iso_all_images(self) -> models.ImageAllISOModel:
        data = await self.client.get("/image/iso/all_images")
        return models.ImageAllISOModel(**data)

    async def last_packages_by_image(
        self,
        branch: str,
        uuid: str,
        packages_limit: int | None = None,
        component: str | None = None,
    ) -> models.LastImagePackagesModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "uuid": uuid,
                "packages_limit": packages_limit,
                "component": component,
            }.items()
            if v is not None
        }
        data = await self.client.get("/image/last_packages_by_image", params)
        return models.LastImagePackagesModel(**data)

    async def last_packages_image_with_cve_fixed(
        self,
        branch: str,
        uuid: str,
        packages_limit: int | None = None,
        component: str | None = None,
    ) -> models.LastImagePackagesModel:
        params = {
            k: v
            for k, v in {
                "branch": branch,
                "uuid": uuid,
                "packages_limit": packages_limit,
                "component": component,
            }.items()
            if v is not None
        }
        data = await self.client.get(
            "/image/last_packages_image_with_cve_fixed", params
        )
        return models.LastImagePackagesModel(**data)


class LicenseInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def license_text(self) -> str:
        return await self.client.get_text("/license")

    async def info(self, license: str) -> models.LicenseInfoModel:
        data = await self.client.get("/license/info", {"license": license})
        return models.LicenseInfoModel(**data)

    async def tokens(self, license: str) -> models.LicenseTokensModel:
        data = await self.client.get("/license/tokens", {"license": license})
        return models.LicenseTokensModel(**data)


class VulnInfo:
    def __init__(self, client: BaseAPI):
        self.client = client

    async def bdu(
        self, vuln_id: str, exclude_json: bool = False
    ) -> models.VulnerabilityInfoModel:
        data = await self.client.get(
            "/vuln/bdu", {"vuln_id": vuln_id, "exclude_json": exclude_json}
        )
        return models.VulnerabilityInfoModel(**data)

    async def bdu_fixes(
        self, vuln_id: str, exclude_json: bool = False
    ) -> models.VulnFixesPackagesModel:
        data = await self.client.get(
            "/vuln/bdu/fixes", {"vuln_id": vuln_id, "exclude_json": exclude_json}
        )
        return models.VulnFixesPackagesModel(**data)

    async def cve(
        self, vuln_id: str, exclude_json: bool = False
    ) -> models.VulnerabilityInfoModel:
        data = await self.client.get(
            "/vuln/cve", {"vuln_id": vuln_id, "exclude_json": exclude_json}
        )
        return models.VulnerabilityInfoModel(**data)

    async def cve_excluded(
        self, vuln_id: str, exclude_json: bool = False
    ) -> models.VulnFixesPackagesModel:
        data = await self.client.get(
            "/vuln/cve/excluded", {"vuln_id": vuln_id, "exclude_json": exclude_json}
        )
        return models.VulnFixesPackagesModel(**data)

    async def cve_fixes(
        self, vuln_id: str, exclude_json: bool = False
    ) -> models.VulnFixesPackagesModel:
        data = await self.client.get(
            "/vuln/cve/fixes", {"vuln_id": vuln_id, "exclude_json": exclude_json}
        )
        return models.VulnFixesPackagesModel(**data)

    async def ghsa(
        self, vuln_id: str, exclude_json: bool = False
    ) -> models.VulnerabilityInfoModel:
        data = await self.client.get(
            "/vuln/ghsa", {"vuln_id": vuln_id, "exclude_json": exclude_json}
        )
        return models.VulnerabilityInfoModel(**data)

    async def ghsa_fixes(
        self, vuln_id: str, exclude_json: bool = False
    ) -> models.VulnFixesPackagesModel:
        data = await self.client.get(
            "/vuln/ghsa/fixes", {"vuln_id": vuln_id, "exclude_json": exclude_json}
        )
        return models.VulnFixesPackagesModel(**data)

    async def task(self, task_id: int) -> models.CveVulnerableTaskModel:
        data = await self.client.get(f"/vuln/task/{task_id}")
        return models.CveVulnerableTaskModel(**data)


class ALTRepoAPI:
    def __init__(self, session: aiohttp.ClientSession, config: "ALTRepoConfig"):
        self.BASE_API_VERSION = "1.25.6"
        self._client = BaseAPI(session, config.api_base_url)
        self.api = APIInfo(self._client)
        self.task = TaskInfo(self._client)
        self.package = PackageInfo(self._client)
        self.packageset = PackagesetInfo(self._client)
        self.acl = AclInfo(self._client)
        self.bug = BugInfo(self._client)
        self.file = FileInfo(self._client)
        self.site = SiteInfo(self._client)
        self.auth = AuthInfo(self._client)
        self.dependencies = DependenciesInfo(self._client)
        self.errata = ErrataInfo(self._client)
        self.export = ExportInfo(self._client)
        self.image = ImageInfo(self._client)
        self.license = LicenseInfo(self._client)
        self.vuln = VulnInfo(self._client)

    async def version(self) -> models.APIVersion:
        return await self.api.version()
