# author Aaron Moser

import paramiko
import socket
import paramiko.ssh_exception

from enum import Enum

from heater.heater_instance import HeaterInstance
from heater.host import Host
from constants import ConnectErrorCode

class HeaterInstanceState(Enum):
    INITIALIZED = 1
    RUNNING = 2
    ERROR = 3
    FINISHED = 4

# Interface to class HeaterInstance
class HeaterInstanceInterface:
    def __init__(self, host: Host):
        self.host = host
        self._heater_instance = HeaterInstance(self.host)
        self._seed = (abs(hash(self.host.hostname + self.host.username)) % 4294967295)
        self._state: HeaterInstanceState = HeaterInstanceState.INITIALIZED

    # Tries to connect to instance via ssh, returns an error code if failing
    def connect(self) -> ConnectErrorCode:
        try:
            if (not self._heater_instance.open):
                self._heater_instance.connect()
                #print("Connected to the SSH server successfully.")
                return ConnectErrorCode.CONNECTED

            else:
                #print("Already connected to SSH server.")
                return ConnectErrorCode.ALREADY_CONNECTED

        except paramiko.ssh_exception.BadHostKeyException as e:
            print(f"BadHostKeyException: The server's host key could not be verified. {e}")
            self._state = HeaterInstanceState.ERROR
            return ConnectErrorCode.UNVERIFIED_KEY
            
        except paramiko.ssh_exception.AuthenticationException as e:
            print(f"AuthenticationException: Authentication failed. {e}")
            self._state = HeaterInstanceState.ERROR
            return ConnectErrorCode.AUTHENTICATION_FAILED
            
        except paramiko.ssh_exception.UnableToAuthenticate as e:
            print(f"UnableToAuthenticate: Authentication failed with the provided strategy. {e}")
            self._state = HeaterInstanceState.ERROR
            return ConnectErrorCode.AUTHENTICATION_FAILED
            
        except socket.error as e:
            print(f"SocketError: A socket error occurred while connecting. {e}")
            self._state = HeaterInstanceState.ERROR
            return ConnectErrorCode.SOCKET_ERROR
            
        except paramiko.ssh_exception.NoValidConnectionsError as e:
            print(f"NoValidConnectionsError: All connection attempts failed. {e}")
            self._state = HeaterInstanceState.ERROR
            return ConnectErrorCode.ALL_CONNECTION_ATTEMPTS_FAILED

        except paramiko.ssh_exception.SSHException as e:
            print(f"SSHException: An error occurred while connecting or establishing an SSH session. {e}")
            self._state = HeaterInstanceState.ERROR
            return ConnectErrorCode.SSH_EXCEPTION

    # Closes ssh connection to instance
    def close(self) -> None:
        self._heater_instance.close()     

    # Sends a task to an instance to produce the required heat
    def run(self, percent: float, seconds: float) -> tuple[int, int, float, float]:
        if (not self._heater_instance.open):
            self.connect()
        result = 0,0,0
        if self._state != HeaterInstanceState.ERROR:
            self._state = HeaterInstanceState.RUNNING
            try:
                result = self._heater_instance.run(percent, seconds)
            except Exception as e:
                print("Connection closed.")
        if self._state == HeaterInstanceState.RUNNING:
            self._state = HeaterInstanceState.FINISHED
        return result

    # Returns host of instance
    def get_host(self) -> Host:
        return self.host
    
    def get_seed(self) -> int:
        return self._seed
    
    def get_state(self) -> HeaterInstanceState:
        return self._state