import requests
import traceback
from pathlib import Path
from requests.exceptions import ConnectionError as RequestsConnectionError, Timeout, RequestException
from urllib3.exceptions import NewConnectionError, MaxRetryError
import tempfile
import os
import io
import zipfile
from datetime import datetime, timezone
from shared.utils import Logger, TokenManager, ConfigManager, LogManager

# =============== Support Service ===============
class SupportService:
    def __init__(self):
        # Logger
        self.logger = Logger.create_logger(logger_name = "Support Service", log_file_name = "validator_app")
        # Helper Class instances
        self.tokenManagerInstance: TokenManager = TokenManager()
        self.configManagerInstance: ConfigManager = ConfigManager()
    
    # ========== Functionality ==========
    def send_zip(self, production: bool) -> None:
        try:
            tokens: dict[str, str] = self.tokenManagerInstance.read_tokens()
            logFilesPaths: list[str] = LogManager().get_logs_paths()
            
            if production == True:
                logSupportUrl: str = self.configManagerInstance.get_value(
                    section = "PROVIDER", 
                    key = self.configManagerInstance.get_value(
                        section = "SERVICES", 
                        key = "production")) + self.configManagerInstance.get_value(section = "METADATA", key = "support")
            else:
                logSupportUrl: str = self.configManagerInstance.get_value(
                    section = "PROVIDER", 
                    key = self.configManagerInstance.get_value(
                        section = "SERVICES", 
                        key = "development")) + self.configManagerInstance.get_value(section = "METADATA", key = "support")
            
            if len(logFilesPaths) == 0:
                return
            
            zip_buffer = io.BytesIO()
            with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
                for file_path in logFilesPaths:
                    zip_file.write(
                        file_path,
                        arcname=os.path.basename(file_path))
            zip_buffer.seek(0)
            files = {
                "context": ("logs.zip", zip_buffer, "application/zip")}

            response = self.request(
                url=logSupportUrl,
                tokens=tokens,
                files=files)

            return response
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.send_zip.__name__}] "
                f"Failed to send the zip with all the log files. \n{traceBack}")
    # ===================================
    
    # ========== Request ==========
    def request(self, url: str, tokens: dict[str, str], files: dict[str, tuple]) -> requests.Response | str:
        """Request data based on url and tokens."""
        try:
            headers = {
                "Authorization": f"JWT {tokens['access']}"
            }

            payload = {
                "source": "Validator App",
                "level": "ERROR",
                "message": "O eroare a fost gasita.",
                "client_timestamp" : datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
            }

            response = requests.post(
                url=url,
                headers=headers,
                data=payload,
                files=files,
                timeout=60
            )
            
            if response.status_code != 201:
                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"
    # =============================
# ===============================================