#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4

"""
The Virtualizor Client API SDK for Python
"""

import json
import requests
import urllib.parse as urp
import phpserialize
import urllib3

# Disable insecure request warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class VirtualizorClient:
    """
    Virtualizor API client
    """

    def __init__(self, url, key, pass_hash, timeout=60, verbose=False, verify_ssl=True):
        """
        Initialize the VirtualizorClient Object
        :param url: root URL, such as 'https://hostname.com:4083'
        :param key: the API KEY
        :param pass_hash: the API Password Hash
        :param timeout: request timeout in seconds
        :param verbose: enable verbose output
        :param verify_ssl: whether to verify SSL certificates (default: True)
        :return: the new object
        """
        self.url = url.rstrip("/")
        self.key = key
        self.pass_hash = pass_hash
        self.timeout = timeout
        self.verbose = verbose
        self.verify_ssl = verify_ssl

    def _post(self, action, post_data=None):
        """
        Post data request to server
        :param action: the API action to perform
        :param post_data: additional parameters
        :return: API response
        """
        # Build URL with authentication parameters
        url = f"{self.url}/index.php?act={action}"
        url += f"&api=serialize&apikey={urp.quote_plus(self.key)}&apipass={urp.quote_plus(self.pass_hash)}"

        try:
            # Prepare request
            headers = {
                "User-Agent": "Virtualizor",
                "Content-Type": "application/x-www-form-urlencoded",
            }

            # Make request
            response = requests.post(
                url,
                data=urp.urlencode(post_data) if post_data else None,
                headers=headers,
                timeout=self.timeout,
                verify=self.verify_ssl,
            )

            if self.verbose:
                print(f"\nDebug: Virtualizor API Request:")
                print(f"URL: {url}")
                print(f"Status Code: {response.status_code}")
                print(f"Raw Response: {response.text}\n")

            if response.status_code != 200:
                return {"error": f"HTTP {response.status_code}: {response.text}"}

            # Parse PHP serialized response
            try:
                if self.verbose:
                    print("\nDebug: Parsing PHP serialized data...")

                data = phpserialize.loads(response.content)
                if self.verbose:
                    print("\nDebug: Parsed data structure:")
                    print(f"Type: {type(data)}")
                    print(
                        f"Keys: {list(data.keys() if isinstance(data, dict) else [])}"
                    )
                    print(f"Data: {data}\n")

                if not isinstance(data, dict):
                    return {"error": "Invalid response format"}

                # Get VPS info
                vps_list = data.get(b"vs", {})
                if self.verbose:
                    print("\nDebug: VPS List:")
                    print(f"Type: {type(vps_list)}")
                    print(
                        f"Keys: {list(vps_list.keys() if isinstance(vps_list, dict) else [])}"
                    )
                    print(f"Data: {vps_list}\n")

                if not vps_list:
                    return {"error": "No VPS information found"}

                # Get first VPS data
                vps_data = next(iter(vps_list.values()))
                if self.verbose:
                    print("\nDebug: VPS Data:")
                    print(f"Type: {type(vps_data)}")
                    print(
                        f"Keys: {list(vps_data.keys() if isinstance(vps_data, dict) else [])}"
                    )
                    print(f"Data: {vps_data}\n")

                # Extract values
                hostname = vps_data.get(b"hostname", b"").decode("utf-8", "ignore")

                # Get IP from ips array
                ips = vps_data.get(b"ips", {})
                if self.verbose:
                    print("\nDebug: IPs Data:")
                    print(f"Type: {type(ips)}")
                    print(f"Keys: {list(ips.keys() if isinstance(ips, dict) else [])}")
                    print(f"Data: {ips}\n")

                try:
                    ipaddress = (
                        next(iter(ips.values())).decode("utf-8", "ignore")
                        if ips
                        else ""
                    )
                except Exception as e:
                    if self.verbose:
                        print(f"Error extracting IP: {str(e)}")
                    ipaddress = ""

                # Get RAM info (MB)
                ram = float(vps_data.get(b"ram", 0)) * 1024 * 1024  # MB to bytes
                ram_used = None  # No reliable way to get RAM usage
                ram_percent = None

                # Get bandwidth info
                bandwidth = (
                    float(vps_data.get(b"bandwidth", 0)) * 1024 * 1024 * 1024
                )  # GB to bytes
                used_bandwidth = (
                    float(vps_data.get(b"used_bandwidth", 0)) * 1024 * 1024 * 1024
                )  # Convert GB to bytes
                bw_percent = (used_bandwidth / bandwidth * 100) if bandwidth > 0 else 0

                # Get disk info from cached_disk
                cached_disk_raw = vps_data.get(b"cached_disk", b"")
                if self.verbose:
                    print("\nDebug: Cached Disk Raw:")
                    print(f"Type: {type(cached_disk_raw)}")
                    print(f"Data: {cached_disk_raw}\n")

                try:
                    # Parse the inner serialized data
                    cached_disk = phpserialize.loads(cached_disk_raw)
                    if self.verbose:
                        print("\nDebug: Parsed Cached Disk:")
                        print(f"Type: {type(cached_disk)}")
                        print(
                            f"Keys: {list(cached_disk.keys() if isinstance(cached_disk, dict) else [])}"
                        )
                        print(f"Data: {cached_disk}\n")

                    # Get disk info
                    disk_info = cached_disk.get(b"disk", {})
                    if self.verbose:
                        print("\nDebug: Disk Info:")
                        print(f"Type: {type(disk_info)}")
                        print(
                            f"Keys: {list(disk_info.keys() if isinstance(disk_info, dict) else [])}"
                        )
                        print(f"Data: {disk_info}\n")

                    # Extract values
                    disk_used = float(disk_info.get(b"Used", 0)) * 1024
                    disk_avail = (
                        float(disk_info.get(b"Available", 0)) * 1024
                    )  # MB to bytes
                    disk_total = disk_used + disk_avail
                    disk_percent = float(disk_info.get(b"Use%", 0))

                except Exception as e:
                    if self.verbose:
                        print(f"\nDebug: Failed to parse cached_disk: {str(e)}")
                        print("Falling back to space field")

                    # Fallback to space field
                    disk_total = (
                        float(vps_data.get(b"space", 0)) * 1024 * 1024 * 1024
                    )  # GB to bytes
                    disk_used = 0
                    disk_percent = 0

                result = {
                    "status": "success",
                    "hostname": hostname,
                    "ipaddress": ipaddress,
                    "bw_used": used_bandwidth,
                    "bw_total": bandwidth,
                    "bw_percent": bw_percent,
                    "mem_used": ram_used,
                    "mem_total": ram,
                    "mem_percent": ram_percent,
                    "disk_used": disk_used,
                    "disk_total": disk_total,
                    "disk_percent": disk_percent,
                }

                if self.verbose:
                    print("\nDebug: Return Value:")
                    print(f"Type: {type(result)}")
                    for key, value in result.items():
                        print(f"{key}: {value}")
                    print()

                return result

            except Exception as e:
                if self.verbose:
                    print(f"\nDebug: Parse error details:")
                    print(f"Error type: {type(e)}")
                    print(f"Error message: {str(e)}")
                    import traceback

                    print(f"Traceback:\n{traceback.format_exc()}")
                return {"error": f"Failed to parse response: {str(e)}"}

        except requests.exceptions.RequestException as e:
            return {"error": str(e)}

    def info(self):
        """
        Retrieve server information including bandwidth, memory, and disk usage
        """
        return self._post("listvs")

    def status(self):
        """
        Retrieve server status
        """
        result = self._post("listvs")
        if "error" in result:
            return result

        try:
            vps_list = result.get("vs", {})
            if not vps_list:
                return {"error": "No VPS information found"}

            vps_data = next(iter(vps_list.values()))
            return {
                "status": "success",
                "statusmsg": "success",
                "state": vps_data.get(
                    "status", "Online" if vps_data.get("status") == 1 else "Offline"
                ),
            }
        except Exception as e:
            return {"error": f"Failed to get status: {str(e)}"}

    def reboot(self):
        """
        Reboot the server
        """
        return self._post("restart", {"do": "1"})

    def boot(self):
        """
        Boot the server
        """
        return self._post("start", {"do": "1"})

    def shutdown(self):
        """
        Shutdown the server
        """
        return self._post("stop", {"do": "1"})

    def command(self, action):
        """
        Run a command raw
        """
        return getattr(self, action)()
