import json
import os
from typing import NamedTuple

from starlette_proxy_router.exceptions import HostNotFoundError
from starlette_proxy_router.types import AuthType, PathConfigs, PathURL, ProxyPathURL
from starlette_proxy_router.validate import ConfigValidate, HostValidate, MethodHTTP


class EndpointArgs(NamedTuple):
    path: PathURL
    target: ProxyPathURL
    method: MethodHTTP
    host: str
    service_name: str
    is_auth: bool = False
    timeout: int = 10
    auth_type: AuthType | None = None
    auth_fields: list[str] | None = None


def _check_exists_path(path_configs: PathConfigs) -> None:
    if isinstance(path_configs, list):
        for path in path_configs:
            if not os.path.exists(path):
                raise FileNotFoundError(f'The specified path does not exist: {path}')
    else:
        if not os.path.exists(path_configs):
            raise FileNotFoundError(f'The specified path does not exist: {path_configs}')


class Parser:
    __slots__ = ("_path_configs",)

    def __init__(self, path_configs: PathConfigs) -> None:
        _check_exists_path(path_configs)
        self._path_configs = path_configs

    def parse_configs(self) -> list[EndpointArgs]:
        if isinstance(self._path_configs, str):
            if os.path.isdir(self._path_configs):
                path_configs = [
                    os.path.join(self._path_configs, path)
                    for path in os.listdir(self._path_configs)
                    if os.path.isfile(os.path.join(self._path_configs, path))
                ]
            else:
                path_configs = [self._path_configs]

        path_configs = [path for path in path_configs if path.endswith('json')]
        if not path_configs:
            raise ValueError("No JSON configs found in specified paths")

        result = []
        for path in path_configs:
            with open(path) as file:
                content = json.load(file)
                data = self._validate_config(content)
                endpoint_args = self._parse(data)
                result.extend(endpoint_args)
        return result

    def _validate_config(self, data: dict) -> ConfigValidate:
        return ConfigValidate.model_validate(data)

    def _validate_and_get_host(self, service_name: str) -> str:
        host = os.environ.get(service_name.upper())
        if host:
            host_validate = HostValidate.model_validate({'host': host})
            return str(host_validate.host)
        raise HostNotFoundError(f"Host for service '{service_name}' not found in env")

    def _parse(self, config_validate: ConfigValidate) -> list[EndpointArgs]:
        args_endpoints = []
        for service in config_validate.services:
            for endpoint in service.endpoints:
                args = EndpointArgs(
                    path=endpoint.path,
                    target=endpoint.target if endpoint.target else endpoint.path,
                    method=endpoint.method or MethodHTTP.GET,
                    is_auth=(
                        endpoint.is_auth
                        if endpoint.is_auth is not None
                        else config_validate.authenfication.default_on_endpoint
                    ),
                    auth_type=config_validate.authenfication.default_type,
                    auth_fields=config_validate.authenfication.fields,
                    service_name=service.service,
                    host=self._validate_and_get_host(service.service),
                    timeout=endpoint.timeout,
                )
                args_endpoints.append(args)
        return args_endpoints
