from __future__ import annotations

from pathlib import Path
import json

from fastgws.helpers import to_snake, unique_name
from fastgws.models import MethodMeta, ParamMeta, ServiceMeta


def load_json(path: str | Path):
    return json.loads(Path(path).read_text())


def load_fixture_doc(path: str | Path):
    return load_json(path)


def normalize_service(entry, doc: dict) -> ServiceMeta:
    methods = []
    _walk_resources(entry.alias, doc.get("resources", {}), [], [], methods)
    methods.sort(key=lambda o: o.dotted_path)
    scopes = doc.get("auth", {}).get("oauth2", {}).get("scopes", {})
    return ServiceMeta(
        alias=entry.alias,
        api_name=entry.api_name,
        version=entry.version,
        title=doc.get("title") or entry.alias,
        description=doc.get("description") or entry.description,
        documentation_link=doc.get("documentationLink"),
        root_url=doc.get("rootUrl", ""),
        base_url=doc.get("baseUrl"),
        service_path=doc.get("servicePath"),
        methods=methods,
        schemas=doc.get("schemas", {}),
        auth_scopes={k: v.get("description", "") for k, v in scopes.items()},
    )


def _walk_resources(alias, resources, py_parts, api_parts, out):
    for api_name, resource in sorted(resources.items()):
        py_name = to_snake(api_name)
        _walk_methods(alias, resource.get("methods", {}), py_parts + [py_name], api_parts + [api_name], out)
        _walk_resources(alias, resource.get("resources", {}), py_parts + [py_name], api_parts + [api_name], out)


def _walk_methods(alias, methods, py_parts, api_parts, out):
    for api_name, method in sorted(methods.items()):
        py_name = to_snake(api_name)
        params = method.get("parameters", {})
        path_params, query_params = _normalize_params(params)
        out.append(
            MethodMeta(
                dotted_path=".".join([alias, *py_parts, py_name]),
                api_dotted_path=".".join([alias, *api_parts, api_name]),
                resource_path=".".join(py_parts),
                py_name=py_name,
                api_name=api_name,
                http_method=method.get("httpMethod", "GET"),
                path=method.get("path", ""),
                flat_path=method.get("flatPath"),
                description=method.get("description", ""),
                path_params=path_params,
                query_params=query_params,
                request_schema_ref=_schema_ref(method.get("request")),
                response_schema_ref=_schema_ref(method.get("response")),
                scopes=method.get("scopes", []),
                supports_media_upload=bool(method.get("supportsMediaUpload")),
                media_upload_path=_upload_path(method.get("mediaUpload")),
                supports_media_download=bool(method.get("supportsMediaDownload")),
            )
        )


def _normalize_params(params):
    seen = set()
    path_params, query_params = [], []
    for api_name, param in sorted(params.items()):
        py_name = unique_name(to_snake(api_name), seen)
        item = ParamMeta(
            py_name=py_name,
            api_name=api_name,
            location=param.get("location", "query"),
            required=bool(param.get("required")),
            repeated=bool(param.get("repeated")),
            type=param.get("type"),
            enum_values=param.get("enum", []) or [],
            description=param.get("description", ""),
            default=param.get("default"),
        )
        (path_params if item.location == "path" else query_params).append(item)
    path_params.sort(key=lambda o: (not o.required, o.py_name))
    query_params.sort(key=lambda o: (not o.required, o.py_name))
    return path_params, query_params


def _schema_ref(node):
    if not node:
        return None
    return node.get("$ref")


def _upload_path(node):
    if not node:
        return None
    return (
        node.get("protocols", {})
        .get("simple", {})
        .get("path")
    )
