import json
from functools import wraps
from string import Template
import typing
import urllib.request
from urllib.parse import urlencode

API_VERSION = "1.2"
ENDPOINT = Template(f"https://api.octranspo1.com/v$version/$method")


def transform_method_name(name: str) -> str:
    return "".join(word.capitalize() for word in name.split("_"))


def transform_argument_name(name: str) -> str:
    parts = name.split("_")
    return parts[0] + "".join(part.capitalize() for part in parts[1:])


def transform_arguments(kwargs) -> dict:
    return {transform_argument_name(key): value for key, value in kwargs.items()}


def make_oc_transport_request(transform_name=False, transform_args=False):
    def outer_func(func):
        @wraps(func)
        def inner_func(self, **kwargs):
            name = func.__name__
            method_name = transform_method_name(name) if transform_name else name
            method_args = transform_arguments(kwargs) if transform_args else kwargs

            req_url = ENDPOINT.substitute(version=self.version, method=method_name)
            req_func = self._build_request(req_url, params=method_args)
            response = req_func()
            return getattr(self, f"_{name}")(response)

        return inner_func

    return outer_func


class OCTransportApi:
    def __init__(
        self, app_id: str, app_key: str, format="json", version=API_VERSION
    ) -> None:
        self._app_id = app_id
        self._app_key = app_key
        self._format = format
        self.version = version

    def _build_request(self, url: str, params):
        params = {
            **{"appID": self._app_id, "apiKey": self._app_key, "format": self._format},
            **params,
        }
        payload = urlencode(params).encode("utf-8")

        def request():
            with urllib.request.urlopen(url, payload) as response:
                return response.read().decode("utf-8")

        return request

    # Autogenerated method return the result of _{their name} method
    def _get_route_summary_for_stop(self, response) -> dict:
        if self._format == 'json':
            return json.loads(response)
        return response

    # Autogenerate method that make API calls that enforce the correct arguements
    @make_oc_transport_request(transform_name=True, transform_args=True)
    def get_route_summary_for_stop(
        self, *, stop_no: int
    ) -> _get_route_summary_for_stop.__annotations__["return"]:
        pass

    def _get_next_trips_for_stop(self, response) -> dict:
        if self._format == 'json':
            return json.loads(response)
        return response

    @make_oc_transport_request(transform_name=True, transform_args=True)
    def get_next_trips_for_stop(
        self, *, route_no: int, stop_no: int
    ) -> _get_next_trips_for_stop.__annotations__["return"]:
        pass

    def _get_next_trips_for_stop_all_routes(self, response) -> dict:
        if self._format == 'json':
            return json.loads(response)
        return response

    @make_oc_transport_request(transform_name=True, transform_args=True)
    def get_next_trips_for_stop_all_routes(
        self, *, stop_no: int
    ) -> _get_next_trips_for_stop_all_routes.__annotations__["return"]:
        pass

    def _gtfs(self, response) -> dict:
        if self._format == 'json':
            return json.loads(response)
        return response

    @make_oc_transport_request(transform_name=True, transform_args=False)
    def gtfs(
        self,
        *,
        table,
        id=None,
        column=None,
        value=None,
        order_by=None,
        direction=None,
        limit=None,
    ) -> _gtfs.__annotations__["return"]:
        pass
