"""NetOrca SDK v2 Authentication."""

import json
import re
from typing import Any, Dict, Optional

import requests
import urllib3
from requests import Response

from netorca_sdk.v2.exceptions import (
    NetorcaAPIError,
    NetorcaException,
)

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

API_VERSION = "/v1"


class NetorcaAuth:
    """Authentication handler for NetOrca API."""

    def __init__(
        self,
        fqdn: str,
        api_key: str,
        verify_ssl: bool = True,
    ) -> None:
        if not api_key:
            raise NetorcaException("Failed to authenticate. API key is required.")
        self.api_key = api_key
        self.fqdn = self.validate_base_url(fqdn)
        self.headers: Dict[str, str] = {
            "content-type": "application/json",
            "Authorization": f"Api-Key {self.api_key}",
        }
        self.verify_ssl = verify_ssl

    def _request(
        self,
        method: str,
        url: str,
        data: Optional[Dict[Any, Any]] = None,
        filters: Optional[Dict[Any, Any]] = None,
    ) -> Response:
        """Internal method to handle HTTP requests."""
        if not url:
            raise NetorcaException("URL not provided!")

        if filters:
            if not isinstance(filters, dict):
                raise NetorcaException("Filters must be a dictionary.")
            url += "?" + "&".join(
                [f"{k}={json.dumps(v) if isinstance(v, dict) else v}" for k, v in filters.items()]
            )

        if data is not None and not isinstance(data, dict):
            raise NetorcaException("Request data must be a dictionary.")

        try:
            response = requests.request(
                method,
                url,
                headers=self.headers,
                data=json.dumps(data) if data else None,
                verify=self.verify_ssl,
            )
            if 200 <= response.status_code < 500:
                return response
            raise NetorcaAPIError(
                f"Failed {method} request. "
                f"URL: {url} "
                f"Response: {response.status_code}. "
                f"Content: {response.content}"
            )
        except requests.exceptions.SSLError:
            raise NetorcaException(f"SSL Error during {method} request to {url}")
        except json.JSONDecodeError:
            raise NetorcaAPIError(f"Failed to decode JSON response from {url}")
        except requests.exceptions.RequestException as e:
            raise NetorcaAPIError(f"Request Error during {method} request to {url}: {str(e)}")

    def get(self, url: str, filters: Optional[Dict[str, Any]] = None) -> Response:
        return self._request("GET", url, filters=filters)

    def post(self, url: str, data: Dict[str, Any]) -> Response:
        return self._request("POST", url, data=data)

    def put(self, url: str, data: Dict[str, Any]) -> Response:
        return self._request("PUT", url, data=data)

    def patch(self, url: str, data: Dict[str, Any]) -> Response:
        return self._request("PATCH", url, data=data)

    def delete(self, url: str) -> Response:
        return self._request("DELETE", url)

    def upload_file(
        self,
        url: str,
        file_path: str,
        upload_filename: str,
        field_name: str = "file",
        content_type: str = "application/octet-stream",
    ) -> Response:
        """Upload a file using multipart/form-data."""
        if not url:
            raise NetorcaException("URL not provided!")
        headers = {k: v for k, v in self.headers.items() if k.lower() != "content-type"}
        try:
            with open(file_path, "rb") as f:
                files = {field_name: (upload_filename, f, content_type)}
                response = requests.request(
                    "POST",
                    url,
                    headers=headers,
                    files=files,
                    verify=self.verify_ssl,
                )
            if 200 <= response.status_code < 500:
                return response
            raise NetorcaAPIError(
                f"Failed upload request. URL: {url} "
                f"Response: {response.status_code}. Content: {response.content}"
            )
        except FileNotFoundError:
            raise NetorcaException(f"File not found: {file_path}")
        except requests.exceptions.RequestException as e:
            raise NetorcaAPIError(f"Request error during upload to {url}: {str(e)}")

    @staticmethod
    def validate_base_url(base_url: str) -> str:
        if not base_url:
            raise NetorcaException("Base URL is empty.")
        base_url = base_url.strip()
        base_url = re.sub(r"/+$", "", base_url)
        base_url = re.sub(f"{API_VERSION}/*$", f"{API_VERSION}", base_url)
        if not base_url.endswith(f"{API_VERSION}"):
            base_url += f"{API_VERSION}"
        return base_url
