import os
import podonos
import requests
import logging
import importlib.metadata

from requests import Response
from typing import Dict, Any, Optional
from packaging.version import Version

from podonos.common.exception import HTTPError
from podonos.common.enum import TerminalColor

logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)

class APIVersion:
    _minimum: Version
    _recommended: Version
    _latest: Version
    
    def __init__(self, minimum: str, recommended: str, latest: str):
        self._minimum = Version(minimum)
        self._recommended = Version(recommended)
        self._latest = Version(latest)
    
    @property
    def minimum(self) -> Version:
        return self._minimum
    
    @property
    def recommended(self) -> Version:
        return self._recommended
    
    @property
    def latest(self) -> Version:
        return self._latest

class APIClient:
    _api_key: str
    _api_url: str
    _headers: Dict[str, str] = {}
    
    def __init__(self, api_key: str, api_url: str):
        self._api_key = api_key
        self._api_url = api_url
        self._headers = {
            "X-API-KEY": self._api_key
        }
    
    @property
    def api_key(self) -> str:
        return self._api_key
    
    @property
    def api_url(self) -> str:
        return self._api_url
    
    def initialize(self) -> bool:
        self._check_minimum_version()
        
        response = self.get("customers/verify/api-key")
        if response.text != "true":
            raise ValueError(f"Invalid API key: {self._api_key}")
        return True
    
    def add_headers(self, key: str, value: str) -> None:
        self._headers[key] = value
    
    def get(self, endpoint: str, params: Optional[Dict[str, str]] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        request_header = self._headers if headers is None else headers
        response = requests.get(f"{self._api_url}/{endpoint}", headers=request_header, params=params)
        return response

    def post(self, endpoint: str, data: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> Response:
        request_header = self._headers if headers is None else headers
        response = requests.post(f"{self._api_url}/{endpoint}", headers=request_header, json=data)
        return response
    
    def put(self, endpoint: str, data: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> Response:
        request_header = self._headers if headers is None else headers
        response = requests.put(f"{self._api_url}/{endpoint}", headers=request_header, json=data)
        return response
    
    def put_file_presigned_url(self, url: str, path: str) -> Response:
        try:
            response = requests.put(url, data=open(path, "rb"), headers={
                "Content-Type": self._set_content_type_by_filename(path)
            })
            return response
        except requests.exceptions.RequestException as e:
            log.error(f"HTTP Error: {e}")
            raise HTTPError(f"Failed to Upload File {path}: {e}", status_code=e.response.status_code if e.response else None)
    
    def put_json_presigned_url(self, url: str, data: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> Response:
        try:
            response = requests.put(url, json=data, headers=headers)
            return response
        except requests.exceptions.RequestException as e:
            log.error(f"HTTP Error: {e}")
            raise HTTPError(f"Failed to Upload JSON {data}: {e}", status_code=e.response.status_code if e.response else None)
        
    def _set_content_type_by_filename(self, path: str) -> str:
        _, ext = os.path.splitext(path)
        if ext == '.wav':
            return "audio/wav"
        elif ext == '.mp3':
            return "audio/mpeg"
        elif ext == '.json':
            return "application/json"
        return "application/octet-stream"
    
    def _check_minimum_version(self) -> bool:
        response = self.get("version/sdk")
        api_version = APIVersion(**response.json())
        
        current_version = self._get_podonos_version()
        log.debug(f'current package version: {current_version}')
        
        if Version(current_version) >= api_version.recommended:
            return True
        
        if Version(current_version) >= api_version.minimum:
            print(
                "The current podonos package version is {current_version} "
                "while a newer version {api_version.latest} is available\n"
                "Please upgrade by 'pip install podonos --upgrade'"
            )
            return True
        
        # This version is lower than the minimum required version. Cannot proceed.
        print(
            TerminalColor.FAIL.value + 
            f"The current podonos package version is {current_version} "
            f"while the minimum supported version is {api_version.minimum}" + TerminalColor.ENDC.value + "\n" +
            TerminalColor.BOLD.value + "Please upgrade" + TerminalColor.ENDC.value + f" by 'pip install podonos --upgrade'"
        )
        raise ValueError(f"Minimum supported version is {api_version.minimum}")

    def _get_podonos_version(self):
        try:
            # Try to get the version using importlib.metadata
            return importlib.metadata.version("podonos")
        except importlib.metadata.PackageNotFoundError:
            # Fallback to __version__ from podonos package if importlib fails
            return podonos.__version__