from typing import (
    Any,
    cast,
    Optional,
)

from starlette.datastructures import URL

from galaxy.exceptions import ObjectNotFound
from galaxy.util.tool_shed.common_util import remove_protocol_and_user_from_clone_url
from galaxy.version import VERSION
from tool_shed.context import ProvidesRepositoriesContext
from tool_shed.structured_app import ToolShedApp
from tool_shed.util.metadata_util import get_current_repository_metadata_for_changeset_revision
from tool_shed.webapp.model import (
    Repository,
    RepositoryMetadata,
)
from tool_shed_client.schema.trs import (
    DescriptorType,
    Tool,
    ToolClass,
    ToolVersion,
)
from tool_shed_client.schema.trs_service_info import (
    Organization,
    Service,
    ServiceType,
)
from tool_shed_client.trs_util import decode_identifier
from .repositories import guid_to_repository

TRS_SERVICE_NAME = "Tool Shed TRS API"
TRS_SERVICE_DESCRIPTION = "Serves tool shed repository tools according to the GA4GH TRS specification"


def service_info(app: ToolShedApp, request_url: URL):
    components = request_url.components
    hostname = components.hostname
    assert hostname
    default_organization_id = ".".join(reversed(hostname.split(".")))
    config = app.config
    organization_id = cast(str, config.ga4gh_service_id or default_organization_id)
    organization_name = cast(str, config.organization_name or organization_id)
    organization_url = cast(str, config.organization_url or f"{components.scheme}://{components.netloc}")

    organization = Organization(
        url=organization_url,
        name=organization_name,
    )
    service_type = ServiceType(
        group="org.ga4gh",
        artifact="trs",
        version="2.1.0",
    )
    extra_kwds = {}
    if environment := config.ga4gh_service_environment:
        extra_kwds["environment"] = environment
    return Service(
        id=organization_id + ".trs",
        name=TRS_SERVICE_NAME,
        description=TRS_SERVICE_DESCRIPTION,
        organization=organization,
        type=service_type,
        version=VERSION,
        **extra_kwds,
    )


def tool_classes() -> list[ToolClass]:
    return [ToolClass(id="galaxy_tool", name="Galaxy Tool", description="Galaxy XML Tools")]


def trs_tool_id_to_guid(trans: ProvidesRepositoriesContext, trs_tool_id: str) -> str:
    guid = decode_identifier(trans.repositories_hostname, trs_tool_id)
    guid = remove_protocol_and_user_from_clone_url(guid)
    return guid


def trs_tool_id_to_repository(trans: ProvidesRepositoriesContext, trs_tool_id: str) -> Repository:
    return guid_to_repository(trans.app, trs_tool_id_to_guid(trans, trs_tool_id))


def get_repository_metadata_by_tool_version(
    app: ToolShedApp, repository: Repository, tool_id: str
) -> dict[str, RepositoryMetadata]:
    versions = {}
    for _, changeset in repository.installable_revisions(app):
        metadata = get_current_repository_metadata_for_changeset_revision(app, repository, changeset)
        tools: Optional[list[dict[str, Any]]] = metadata.metadata.get("tools")
        if not tools:
            continue
        for tool_metadata in tools:
            if tool_metadata["id"] != tool_id:
                continue
            versions[tool_metadata["version"]] = metadata
    return versions


def get_tools_for(repository_metadata: RepositoryMetadata) -> list[dict[str, Any]]:
    tools: Optional[list[dict[str, Any]]] = repository_metadata.metadata.get("tools")
    assert tools
    return tools


def trs_tool_id_to_repository_metadata(
    trans: ProvidesRepositoriesContext, trs_tool_id: str
) -> tuple[Repository, dict[str, RepositoryMetadata]]:
    tool_guid = decode_identifier(trans.repositories_hostname, trs_tool_id)
    tool_guid = remove_protocol_and_user_from_clone_url(tool_guid)
    _, tool_id = tool_guid.rsplit("/", 1)
    repository = guid_to_repository(trans.app, tool_guid)
    app = trans.app
    versions: dict[str, RepositoryMetadata] = get_repository_metadata_by_tool_version(app, repository, tool_id)
    if not versions:
        raise ObjectNotFound()

    return repository, versions


def get_tool(trans: ProvidesRepositoriesContext, trs_tool_id: str) -> Tool:
    guid = decode_identifier(trans.repositories_hostname, trs_tool_id)
    guid = remove_protocol_and_user_from_clone_url(guid)
    repo_metadata = trs_tool_id_to_repository_metadata(trans, trs_tool_id)
    repository, metadata_by_version = repo_metadata

    repo_owner = repository.user.username
    aliases: list[str] = [guid]
    hostname = remove_protocol_and_user_from_clone_url(trans.repositories_hostname)
    url = f"https://{hostname}/repos/{repo_owner}/{repository.name}"

    versions: list[ToolVersion] = []
    for tool_version_str, _ in metadata_by_version.items():
        version_url = url  # TODO:
        tool_version = ToolVersion(
            author=[repo_owner],
            containerfile=False,
            descriptor_type=[DescriptorType.GALAXY],
            id=tool_version_str,
            url=version_url,
            verified=False,
        )
        versions.append(tool_version)
    return Tool(
        aliases=aliases,
        id=trs_tool_id,
        url=url,
        toolclass=tool_classes()[0],
        organization=repo_owner,
        versions=versions,
    )
