import requests
from typing import Optional

session = requests.Session()

# Global host and port
host = 'demo.ivoryos.ai'
port = 'None'

# Host and port setters
def set_host(self, value):
    global host
    host = value

def set_port(self, value):
    global port
    port = value


class Weigh_balance:
    """Auto-generated API client for weigh_balance operations."""
    global host
    global port
    url = f"http://{host}:{port}/ivoryos/instruments/deck.weigh_balance"

    def __init__(self, username=None, password=None):
        """Initialize the client with authentication."""
        self.username = username
        self.password = password
        self._auth()

    def _auth(self):
        global host
        global port
        username = self.username or 'admin'
        password = self.password or 'admin'
        res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
        if res.status_code == 200:
            return
        else:
            session.post(
                f'http://{host}:{port}/ivoryos/auth/login',
                data={"username": username, "password": password}
            )
            res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
            if res.status_code != 200:
                raise Exception("Authentication failed")
                    
    def _call(self, payload):
        """Make API call with error handling."""
        def sanitize(obj):
            if isinstance(obj, Enum):
                return obj.value
            elif isinstance(obj, list):
                return [sanitize(item) for item in obj]
            elif isinstance(obj, dict):
                return {k: sanitize(v) for k, v in obj.items()}
            return obj

        payload = sanitize(payload)

        res = session.post(self.url, json=payload, allow_redirects=False)
            # Handle 302 redirect (likely auth issue)
        if res.status_code == 302:
            try:
                self._auth()
                res = session.post(self.url, json=payload, allow_redirects=False)
            except Exception as e:
                raise Exception(
                    "Authentication failed during re-attempt. "
                    "Please check your credentials or connection."
                ) from e
        res.raise_for_status()
        data = res.json()
        if not data.get('success'):
            raise Exception(data.get('output', "Unknown API error."))
        return data.get('output')

    def get_weight_mg(self):
        payload = {"hidden_name": "get_weight_mg"}
        return self._call(payload)

    def zero(self):
        payload = {"hidden_name": "zero"}
        return self._call(payload)


class Robotic_arm:
    """Auto-generated API client for robotic_arm operations."""
    global host
    global port
    url = f"http://{host}:{port}/ivoryos/instruments/deck.robotic_arm"

    def __init__(self, username=None, password=None):
        """Initialize the client with authentication."""
        self.username = username
        self.password = password
        self._auth()

    def _auth(self):
        global host
        global port
        username = self.username or 'admin'
        password = self.password or 'admin'
        res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
        if res.status_code == 200:
            return
        else:
            session.post(
                f'http://{host}:{port}/ivoryos/auth/login',
                data={"username": username, "password": password}
            )
            res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
            if res.status_code != 200:
                raise Exception("Authentication failed")
                    
    def _call(self, payload):
        """Make API call with error handling."""
        def sanitize(obj):
            if isinstance(obj, Enum):
                return obj.value
            elif isinstance(obj, list):
                return [sanitize(item) for item in obj]
            elif isinstance(obj, dict):
                return {k: sanitize(v) for k, v in obj.items()}
            return obj

        payload = sanitize(payload)

        res = session.post(self.url, json=payload, allow_redirects=False)
            # Handle 302 redirect (likely auth issue)
        if res.status_code == 302:
            try:
                self._auth()
                res = session.post(self.url, json=payload, allow_redirects=False)
            except Exception as e:
                raise Exception(
                    "Authentication failed during re-attempt. "
                    "Please check your credentials or connection."
                ) from e
        res.raise_for_status()
        data = res.json()
        if not data.get('success'):
            raise Exception(data.get('output', "Unknown API error."))
        return data.get('output')

    def pick_up_vial_from_tray(self, vial_number: Optional[int] = None):
        payload = {"hidden_name": "pick_up_vial_from_tray"}
        payload["vial_number"] = vial_number
        return self._call(payload)

    def pick_up_vial_in_analysis_station(self):
        payload = {"hidden_name": "pick_up_vial_in_analysis_station"}
        return self._call(payload)

    def pick_up_vial_on_capping_station(self):
        payload = {"hidden_name": "pick_up_vial_on_capping_station"}
        return self._call(payload)

    def pick_up_vial_on_liquid_addition_station(self):
        payload = {"hidden_name": "pick_up_vial_on_liquid_addition_station"}
        return self._call(payload)

    def pick_up_vial_on_solid_addition_station(self):
        payload = {"hidden_name": "pick_up_vial_on_solid_addition_station"}
        return self._call(payload)

    def pick_up_vial_on_stir_plate(self):
        payload = {"hidden_name": "pick_up_vial_on_stir_plate"}
        return self._call(payload)

    def pick_up_vial_on_weigh_balance(self):
        payload = {"hidden_name": "pick_up_vial_on_weigh_balance"}
        return self._call(payload)

    def place_vial_in_analysis_station(self):
        payload = {"hidden_name": "place_vial_in_analysis_station"}
        return self._call(payload)

    def place_vial_in_tray(self, vial_number: Optional[int] = None):
        payload = {"hidden_name": "place_vial_in_tray"}
        payload["vial_number"] = vial_number
        return self._call(payload)

    def place_vial_on_capping_station(self):
        payload = {"hidden_name": "place_vial_on_capping_station"}
        return self._call(payload)

    def place_vial_on_liquid_addition_station(self):
        payload = {"hidden_name": "place_vial_on_liquid_addition_station"}
        return self._call(payload)

    def place_vial_on_solid_addition_station(self):
        payload = {"hidden_name": "place_vial_on_solid_addition_station"}
        return self._call(payload)

    def place_vial_on_stir_plate(self):
        payload = {"hidden_name": "place_vial_on_stir_plate"}
        return self._call(payload)

    def place_vial_on_weigh_balance(self):
        payload = {"hidden_name": "place_vial_on_weigh_balance"}
        return self._call(payload)


class Stir_plate:
    """Auto-generated API client for stir_plate operations."""
    global host
    global port
    url = f"http://{host}:{port}/ivoryos/instruments/deck.stir_plate"

    def __init__(self, username=None, password=None):
        """Initialize the client with authentication."""
        self.username = username
        self.password = password
        self._auth()

    def _auth(self):
        global host
        global port
        username = self.username or 'admin'
        password = self.password or 'admin'
        res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
        if res.status_code == 200:
            return
        else:
            session.post(
                f'http://{host}:{port}/ivoryos/auth/login',
                data={"username": username, "password": password}
            )
            res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
            if res.status_code != 200:
                raise Exception("Authentication failed")
                    
    def _call(self, payload):
        """Make API call with error handling."""
        def sanitize(obj):
            if isinstance(obj, Enum):
                return obj.value
            elif isinstance(obj, list):
                return [sanitize(item) for item in obj]
            elif isinstance(obj, dict):
                return {k: sanitize(v) for k, v in obj.items()}
            return obj

        payload = sanitize(payload)

        res = session.post(self.url, json=payload, allow_redirects=False)
            # Handle 302 redirect (likely auth issue)
        if res.status_code == 302:
            try:
                self._auth()
                res = session.post(self.url, json=payload, allow_redirects=False)
            except Exception as e:
                raise Exception(
                    "Authentication failed during re-attempt. "
                    "Please check your credentials or connection."
                ) from e
        res.raise_for_status()
        data = res.json()
        if not data.get('success'):
            raise Exception(data.get('output', "Unknown API error."))
        return data.get('output')

    def start_stirring(self):
        payload = {"hidden_name": "start_stirring"}
        return self._call(payload)

    def stop_stirring(self):
        payload = {"hidden_name": "stop_stirring"}
        return self._call(payload)


class Solid_addition_station:
    """Auto-generated API client for solid_addition_station operations."""
    global host
    global port
    url = f"http://{host}:{port}/ivoryos/instruments/deck.solid_addition_station"

    def __init__(self, username=None, password=None):
        """Initialize the client with authentication."""
        self.username = username
        self.password = password
        self._auth()

    def _auth(self):
        global host
        global port
        username = self.username or 'admin'
        password = self.password or 'admin'
        res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
        if res.status_code == 200:
            return
        else:
            session.post(
                f'http://{host}:{port}/ivoryos/auth/login',
                data={"username": username, "password": password}
            )
            res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
            if res.status_code != 200:
                raise Exception("Authentication failed")
                    
    def _call(self, payload):
        """Make API call with error handling."""
        def sanitize(obj):
            if isinstance(obj, Enum):
                return obj.value
            elif isinstance(obj, list):
                return [sanitize(item) for item in obj]
            elif isinstance(obj, dict):
                return {k: sanitize(v) for k, v in obj.items()}
            return obj

        payload = sanitize(payload)

        res = session.post(self.url, json=payload, allow_redirects=False)
            # Handle 302 redirect (likely auth issue)
        if res.status_code == 302:
            try:
                self._auth()
                res = session.post(self.url, json=payload, allow_redirects=False)
            except Exception as e:
                raise Exception(
                    "Authentication failed during re-attempt. "
                    "Please check your credentials or connection."
                ) from e
        res.raise_for_status()
        data = res.json()
        if not data.get('success'):
            raise Exception(data.get('output', "Unknown API error."))
        return data.get('output')

    def add_solid(self, mass_mg: float):
        payload = {"hidden_name": "add_solid"}
        payload["mass_mg"] = mass_mg
        return self._call(payload)


class Sample_analysis_station:
    """Auto-generated API client for sample_analysis_station operations."""
    global host
    global port
    url = f"http://{host}:{port}/ivoryos/instruments/deck.sample_analysis_station"

    def __init__(self, username=None, password=None):
        """Initialize the client with authentication."""
        self.username = username
        self.password = password
        self._auth()

    def _auth(self):
        global host
        global port
        username = self.username or 'admin'
        password = self.password or 'admin'
        res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
        if res.status_code == 200:
            return
        else:
            session.post(
                f'http://{host}:{port}/ivoryos/auth/login',
                data={"username": username, "password": password}
            )
            res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
            if res.status_code != 200:
                raise Exception("Authentication failed")
                    
    def _call(self, payload):
        """Make API call with error handling."""
        def sanitize(obj):
            if isinstance(obj, Enum):
                return obj.value
            elif isinstance(obj, list):
                return [sanitize(item) for item in obj]
            elif isinstance(obj, dict):
                return {k: sanitize(v) for k, v in obj.items()}
            return obj

        payload = sanitize(payload)

        res = session.post(self.url, json=payload, allow_redirects=False)
            # Handle 302 redirect (likely auth issue)
        if res.status_code == 302:
            try:
                self._auth()
                res = session.post(self.url, json=payload, allow_redirects=False)
            except Exception as e:
                raise Exception(
                    "Authentication failed during re-attempt. "
                    "Please check your credentials or connection."
                ) from e
        res.raise_for_status()
        data = res.json()
        if not data.get('success'):
            raise Exception(data.get('output', "Unknown API error."))
        return data.get('output')

    def analyze(self, input_1: Optional[float] = None, input_2: Optional[float] = None) -> float:
        """Return a Branin signal value
If no inputs are given, random values
    input_1 in [-5, 10]
    input_2 in [ 0, 15]

:param input_1: optional, float
:param input_2: optional, float
:return:"""
        payload = {"hidden_name": "analyze"}
        payload["input_1"] = input_1
        payload["input_2"] = input_2
        return self._call(payload)


class Capping_station:
    """Auto-generated API client for capping_station operations."""
    global host
    global port
    url = f"http://{host}:{port}/ivoryos/instruments/deck.capping_station"

    def __init__(self, username=None, password=None):
        """Initialize the client with authentication."""
        self.username = username
        self.password = password
        self._auth()

    def _auth(self):
        global host
        global port
        username = self.username or 'admin'
        password = self.password or 'admin'
        res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
        if res.status_code == 200:
            return
        else:
            session.post(
                f'http://{host}:{port}/ivoryos/auth/login',
                data={"username": username, "password": password}
            )
            res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
            if res.status_code != 200:
                raise Exception("Authentication failed")
                    
    def _call(self, payload):
        """Make API call with error handling."""
        def sanitize(obj):
            if isinstance(obj, Enum):
                return obj.value
            elif isinstance(obj, list):
                return [sanitize(item) for item in obj]
            elif isinstance(obj, dict):
                return {k: sanitize(v) for k, v in obj.items()}
            return obj

        payload = sanitize(payload)

        res = session.post(self.url, json=payload, allow_redirects=False)
            # Handle 302 redirect (likely auth issue)
        if res.status_code == 302:
            try:
                self._auth()
                res = session.post(self.url, json=payload, allow_redirects=False)
            except Exception as e:
                raise Exception(
                    "Authentication failed during re-attempt. "
                    "Please check your credentials or connection."
                ) from e
        res.raise_for_status()
        data = res.json()
        if not data.get('success'):
            raise Exception(data.get('output', "Unknown API error."))
        return data.get('output')

    def cap_vial(self):
        payload = {"hidden_name": "cap_vial"}
        return self._call(payload)

    def uncap_vial(self):
        payload = {"hidden_name": "uncap_vial"}
        return self._call(payload)


class Liquid_addition_station:
    """Auto-generated API client for liquid_addition_station operations."""
    global host
    global port
    url = f"http://{host}:{port}/ivoryos/instruments/deck.liquid_addition_station"

    def __init__(self, username=None, password=None):
        """Initialize the client with authentication."""
        self.username = username
        self.password = password
        self._auth()

    def _auth(self):
        global host
        global port
        username = self.username or 'admin'
        password = self.password or 'admin'
        res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
        if res.status_code == 200:
            return
        else:
            session.post(
                f'http://{host}:{port}/ivoryos/auth/login',
                data={"username": username, "password": password}
            )
            res = session.get(f'http://{host}:{port}/ivoryos/', allow_redirects=False)
            if res.status_code != 200:
                raise Exception("Authentication failed")
                    
    def _call(self, payload):
        """Make API call with error handling."""
        def sanitize(obj):
            if isinstance(obj, Enum):
                return obj.value
            elif isinstance(obj, list):
                return [sanitize(item) for item in obj]
            elif isinstance(obj, dict):
                return {k: sanitize(v) for k, v in obj.items()}
            return obj

        payload = sanitize(payload)

        res = session.post(self.url, json=payload, allow_redirects=False)
            # Handle 302 redirect (likely auth issue)
        if res.status_code == 302:
            try:
                self._auth()
                res = session.post(self.url, json=payload, allow_redirects=False)
            except Exception as e:
                raise Exception(
                    "Authentication failed during re-attempt. "
                    "Please check your credentials or connection."
                ) from e
        res.raise_for_status()
        data = res.json()
        if not data.get('success'):
            raise Exception(data.get('output', "Unknown API error."))
        return data.get('output')

    def add_liquid(self, volume_ml: float):
        payload = {"hidden_name": "add_liquid"}
        payload["volume_ml"] = volume_ml
        return self._call(payload)


# Default instances for convenience
weigh_balance = Weigh_balance()
robotic_arm = Robotic_arm()
stir_plate = Stir_plate()
solid_addition_station = Solid_addition_station()
sample_analysis_station = Sample_analysis_station()
capping_station = Capping_station()
liquid_addition_station = Liquid_addition_station()
