import requests
import traceback
from pathlib import Path
from requests.exceptions import ConnectionError as RequestsConnectionError, Timeout, RequestException
from urllib3.exceptions import NewConnectionError, MaxRetryError

from shared.utils import Logger, TokenManager, ConfigManager

# =============== Validation Packages Service ===============
class ValidationKitService:
    def __init__(self):
        # Logger
        self.logger = Logger.create_logger(logger_name = "Validation Packages Service", log_file_name = "validator_app")
        
        # Class Instances
        self.tokenManager = TokenManager()
        self.configManager = ConfigManager()
        
        # In memory
        self._validationKitsDevelopment: list[dict[str, str]] = []
        self._validationKitsPublic: list[dict[str, str]] = []
    
    # ========== On Start-Up ==========
    def get_all_validation_kits(self, production: bool) -> None:
        try:
            tokens: dict[str, str] = self.tokenManager.read_tokens()
            currentKitVersion: str = self.configManager.get_value(section = "VERSION", key = "kit")
            
            if production == True:
                self._validationKitsPublic.clear()
                kitVersionsRequestUrl: str = self.configManager.get_value(
                    section = "PROVIDER", key = self.configManager.get_value(section = "SERVICES", key = "production")) + self.configManager.get_value(section = "METADATA", key = "latest_version")
                mediaURL: str = self.configManager.get_value(section = "MEDIA", key = self.configManager.get_value(section = "SERVICES", key = "production"))
                
                kitVersionsResponse = self.request(url = kitVersionsRequestUrl, tokens = tokens)
                if type(kitVersionsResponse) == str:
                    return []
                
                kitVersions: list[dict[str, str]] = kitVersionsResponse.json()
                
                for kit in kitVersions:
                    yyyy, mm, dd = kit["lansat"].split('-')
                    formattedDate = f"{dd}/{mm}/{yyyy}"
                    validationKitData = {
                        "Version" : kit["versiune"],
                        "Title" : kit["titlu"],
                        "ReleaseDate" : formattedDate,
                        "DownloadLink" : mediaURL + kit["file"] if kit["file"] is not None else None,
                        "Downloadable" : False if kit["versiune"] == currentKitVersion else True
                    }
                    if validationKitData not in self._validationKitsPublic:
                        self._validationKitsPublic.append(validationKitData)
                
                self._validationKitsPublic.sort(key=lambda k: self.version_key(k["Version"]), reverse = True)
                
            else:
                self._validationKitsDevelopment.clear()
                kitVersionsRequestUrl: str = self.configManager.get_value(
                    section = "PROVIDER", key = self.configManager.get_value(section = "SERVICES", key = "development")) + self.configManager.get_value(section = "METADATA", key = "latest_version")
                mediaURL: str = self.configManager.get_value(section = "MEDIA", key = self.configManager.get_value(section = "SERVICES", key = "development"))
                
                kitVersionsResponse = self.request(url = kitVersionsRequestUrl, tokens = tokens)
                if type(kitVersionsResponse) == str:
                    return []
                
                kitVersions: list[dict[str, str]] = kitVersionsResponse.json()
                
                for kit in kitVersions:
                    yyyy, mm, dd = kit["lansat"].split('-')
                    
                    formattedDate = f"{dd}/{mm}/{yyyy}"
                    validationKitData = {
                        "Version" : kit["versiune"],
                        "Title" : kit["titlu"],
                        "ReleaseDate" : formattedDate,
                        "DownloadLink" : mediaURL + kit["file"] if kit["file"] is not None else None,
                        "Downloadable" : False if kit["versiune"] == currentKitVersion else True
                    }
                    if validationKitData not in self._validationKitsDevelopment:
                        self._validationKitsDevelopment.append(validationKitData)
                
                self._validationKitsDevelopment.sort(key=lambda k: self.version_key(k["Version"]), reverse = True)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.get_all_validation_kits.__name__}] "
                f"Failed to get all the validation kits. \n{traceBack}")
    # =================================
    
    # ========== Functionality ==========
    def version_key(self, version_str: str):
        # Remove last character
        v = version_str[:-1]

        # Split by dots and convert to integers
        parts = v.split(".")
        int_parts = []
        for part in parts:
            try:
                int_parts.append(int(part))
            except ValueError:
                int_parts.append(0)  # fallback if part is not an integer
        return tuple(int_parts)
    # ===================================
    
    # ========== Request ==========
    def request(self, url: str, tokens: dict[str, str]) -> requests.Response | str:
        """Request data based on url and tokens."""
        try:
            response = requests.get(
                url = url,
                headers = {'Authorization': f'JWT {tokens["access"]}'},
                timeout = 60
            )
            
            if response.status_code != 200:
                return "Error"
            
            return response
        
        except NewConnectionError as e:
            # This catches DNS failures, refused connections, etc.
            self.logger.error(f"[{self.__class__.__name__}.{self.request.__name__}] No internet connection or host unreachable: {e}")
            return "ConnectionError"
        
        except MaxRetryError as e:
            # This catches DNS failures, refused connections, etc.
            self.logger.error(f"[{self.__class__.__name__}.{self.request.__name__}] No internet connection or host unreachable: {e}")
            return "ConnectionError"
        
        except RequestsConnectionError as e:
            # This catches DNS failures, refused connections, etc.
            self.logger.error(f"[{self.__class__.__name__}.{self.request.__name__}] No internet connection or host unreachable: {e}")
            return "ConnectionError"
        
        except ConnectionError as e:
            # This catches DNS failures, refused connections, etc.
            self.logger.error(f"[{self.__class__.__name__}.{self.request.__name__}] No internet connection or host unreachable: {e}")
            return "ConnectionError"
            
        except Timeout as e:
            self.logger.error(f"[{self.__class__.__name__}.{self.request.__name__}] Request timed out: {e}")
            return "ConnectionError"
            
        except RequestException as e:
            # Catches all other requests-related errors
            self.logger.error(f"[{self.__class__.__name__}.{self.request.__name__}] Request failed: {e}")
            return "ConnectionError"
            
        except Exception as e:
            self.logger.error(f"[Class '{self.__class__.__name__}' - Function '{self.request.__name__}'] An error has occurred: {e}")
            return "Error"
    # =============================
# ===========================================================
# ===========================================================