"""Route registrar for model endpoints."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Callable, Iterable, Mapping

from aiohttp import web


@dataclass(frozen=True)
class RouteDefinition:
    """Declarative definition for a HTTP route."""

    method: str
    path_template: str
    handler_name: str

    def build_path(self, prefix: str) -> str:
        return self.path_template.replace("{prefix}", prefix)


COMMON_ROUTE_DEFINITIONS: tuple[RouteDefinition, ...] = (
    RouteDefinition("GET", "/api/lm/{prefix}/list", "get_models"),
    RouteDefinition("GET", "/api/lm/{prefix}/excluded", "get_excluded_models"),
    RouteDefinition("POST", "/api/lm/{prefix}/delete", "delete_model"),
    RouteDefinition("POST", "/api/lm/{prefix}/exclude", "exclude_model"),
    RouteDefinition("POST", "/api/lm/{prefix}/unexclude", "unexclude_model"),
    RouteDefinition("POST", "/api/lm/{prefix}/fetch-civitai", "fetch_civitai"),
    RouteDefinition("POST", "/api/lm/{prefix}/fetch-all-civitai", "fetch_all_civitai"),
    RouteDefinition("POST", "/api/lm/{prefix}/relink-civitai", "relink_civitai"),
    RouteDefinition("POST", "/api/lm/{prefix}/replace-preview", "replace_preview"),
    RouteDefinition(
        "POST", "/api/lm/{prefix}/set-preview-from-url", "set_preview_from_url"
    ),
    RouteDefinition("POST", "/api/lm/{prefix}/save-metadata", "save_metadata"),
    RouteDefinition("POST", "/api/lm/{prefix}/add-tags", "add_tags"),
    RouteDefinition("POST", "/api/lm/{prefix}/rename", "rename_model"),
    RouteDefinition("POST", "/api/lm/{prefix}/bulk-delete", "bulk_delete_models"),
    RouteDefinition("POST", "/api/lm/{prefix}/verify-duplicates", "verify_duplicates"),
    RouteDefinition("POST", "/api/lm/{prefix}/move_model", "move_model"),
    RouteDefinition("POST", "/api/lm/{prefix}/move_models_bulk", "move_models_bulk"),
    RouteDefinition("GET", "/api/lm/{prefix}/auto-organize", "auto_organize_models"),
    RouteDefinition("POST", "/api/lm/{prefix}/auto-organize", "auto_organize_models"),
    RouteDefinition(
        "GET", "/api/lm/{prefix}/auto-organize-progress", "get_auto_organize_progress"
    ),
    RouteDefinition("GET", "/api/lm/{prefix}/top-tags", "get_top_tags"),
    RouteDefinition("GET", "/api/lm/{prefix}/search-tags", "search_tags"),
    RouteDefinition("GET", "/api/lm/{prefix}/base-models", "get_base_models"),
    RouteDefinition("GET", "/api/lm/{prefix}/model-types", "get_model_types"),
    RouteDefinition("GET", "/api/lm/{prefix}/scan", "scan_models"),
    RouteDefinition("GET", "/api/lm/{prefix}/roots", "get_model_roots"),
    RouteDefinition("GET", "/api/lm/{prefix}/folders", "get_folders"),
    RouteDefinition("GET", "/api/lm/{prefix}/folder-tree", "get_folder_tree"),
    RouteDefinition(
        "GET", "/api/lm/{prefix}/unified-folder-tree", "get_unified_folder_tree"
    ),
    RouteDefinition("GET", "/api/lm/{prefix}/find-duplicates", "find_duplicate_models"),
    RouteDefinition(
        "GET", "/api/lm/{prefix}/find-filename-conflicts", "find_filename_conflicts"
    ),
    RouteDefinition("GET", "/api/lm/{prefix}/get-notes", "get_model_notes"),
    RouteDefinition("GET", "/api/lm/{prefix}/preview-url", "get_model_preview_url"),
    RouteDefinition("GET", "/api/lm/{prefix}/civitai-url", "get_model_civitai_url"),
    RouteDefinition("GET", "/api/lm/{prefix}/metadata", "get_model_metadata"),
    RouteDefinition(
        "GET", "/api/lm/{prefix}/model-description", "get_model_description"
    ),
    RouteDefinition("GET", "/api/lm/{prefix}/relative-paths", "get_relative_paths"),
    RouteDefinition(
        "GET", "/api/lm/{prefix}/civitai/versions/{model_id}", "get_civitai_versions"
    ),
    RouteDefinition(
        "GET",
        "/api/lm/{prefix}/civitai/model/version/{modelVersionId}",
        "get_civitai_model_by_version",
    ),
    RouteDefinition(
        "GET", "/api/lm/{prefix}/civitai/model/hash/{hash}", "get_civitai_model_by_hash"
    ),
    RouteDefinition(
        "POST", "/api/lm/{prefix}/updates/refresh", "refresh_model_updates"
    ),
    RouteDefinition(
        "POST",
        "/api/lm/{prefix}/updates/fetch-missing-license",
        "fetch_missing_civitai_license_data",
    ),
    RouteDefinition(
        "POST", "/api/lm/{prefix}/updates/ignore", "set_model_update_ignore"
    ),
    RouteDefinition(
        "POST", "/api/lm/{prefix}/updates/ignore-version", "set_version_update_ignore"
    ),
    RouteDefinition(
        "GET", "/api/lm/{prefix}/updates/status/{model_id}", "get_model_update_status"
    ),
    RouteDefinition(
        "GET", "/api/lm/{prefix}/updates/versions/{model_id}", "get_model_versions"
    ),
    RouteDefinition("POST", "/api/lm/download-model", "download_model"),
    RouteDefinition("GET", "/api/lm/download-model-get", "download_model_get"),
    RouteDefinition("GET", "/api/lm/cancel-download-get", "cancel_download_get"),
    RouteDefinition("GET", "/api/lm/skip-download", "skip_download_get"),
    RouteDefinition("GET", "/api/lm/pause-download", "pause_download_get"),
    RouteDefinition("GET", "/api/lm/resume-download", "resume_download_get"),
    RouteDefinition(
        "GET", "/api/lm/download-progress/{download_id}", "get_download_progress"
    ),
    RouteDefinition("GET", "/api/lm/downloads/queue", "get_download_queue"),
    RouteDefinition("GET", "/api/lm/downloads/queue/add", "add_to_download_queue"),
    RouteDefinition(
        "GET", "/api/lm/downloads/queue/remove", "remove_from_download_queue"
    ),
    RouteDefinition(
        "GET", "/api/lm/downloads/queue/move-to-top", "move_queue_item_to_top"
    ),
    RouteDefinition(
        "GET", "/api/lm/downloads/queue/move-to-end", "move_queue_item_to_end"
    ),
    RouteDefinition(
        "GET", "/api/lm/downloads/queue/clear", "clear_download_queue"
    ),
    RouteDefinition("GET", "/api/lm/downloads/history", "get_download_history"),
    RouteDefinition(
        "GET", "/api/lm/downloads/history/clear", "clear_download_history"
    ),
    RouteDefinition(
        "GET", "/api/lm/downloads/history/delete", "delete_download_history_item"
    ),
    RouteDefinition(
        "GET", "/api/lm/downloads/history/retry", "retry_download_from_history"
    ),
    RouteDefinition(
        "GET", "/api/lm/downloads/history/retry-all", "retry_all_failed_downloads"
    ),
    RouteDefinition("GET", "/api/lm/downloads/stats", "get_download_stats"),
    RouteDefinition(
        "GET", "/api/lm/downloads/queue/complete", "complete_download_in_queue"
    ),
    RouteDefinition(
        "GET", "/api/lm/downloads/queue/status", "update_download_queue_status"
    ),
    RouteDefinition("POST", "/api/lm/{prefix}/cancel-task", "cancel_task"),
    RouteDefinition("GET", "/{prefix}", "handle_models_page"),
)


class ModelRouteRegistrar:
    """Bind declarative definitions to an aiohttp router."""

    _METHOD_MAP = {
        "GET": "add_get",
        "POST": "add_post",
        "PUT": "add_put",
        "DELETE": "add_delete",
    }

    def __init__(self, app: web.Application) -> None:
        self._app = app

    def register_common_routes(
        self,
        prefix: str,
        handler_lookup: Mapping[str, Callable[[web.Request], object]],
        *,
        definitions: Iterable[RouteDefinition] = COMMON_ROUTE_DEFINITIONS,
    ) -> None:
        for definition in definitions:
            self._bind_route(
                definition.method,
                definition.build_path(prefix),
                handler_lookup[definition.handler_name],
            )

    def add_route(self, method: str, path: str, handler: Callable[..., Any]) -> None:
        self._bind_route(method, path, handler)

    def add_prefixed_route(
        self, method: str, path_template: str, prefix: str, handler: Callable[..., Any]
    ) -> None:
        self._bind_route(method, path_template.replace("{prefix}", prefix), handler)

    def _bind_route(self, method: str, path: str, handler: Callable[..., Any]) -> None:
        add_method_name = self._METHOD_MAP[method.upper()]
        add_method = getattr(self._app.router, add_method_name)
        add_method(path, handler)
