"""System information retrieval functions with caching and parallel execution."""
import subprocess
import socket
import time
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed


class SystemInfoCache:
    """Thread-safe cache with TTL support."""

    def __init__(self, ttl=60):
        self._cache = {}
        self._timestamps = {}
        self._lock = threading.Lock()
        self._ttl = ttl

    def get(self, key, fetch_func):
        with self._lock:
            if self._is_expired(key):
                self._cache[key] = fetch_func()
                self._timestamps[key] = time.time()
            return self._cache[key]

    def _is_expired(self, key):
        if key not in self._cache:
            return True
        if time.time() - self._timestamps.get(key, 0) > self._ttl:
            return True
        return False

    def clear(self):
        with self._lock:
            self._cache.clear()
            self._timestamps.clear()


_cache = SystemInfoCache(ttl=60)


def _fetch_username():
    try:
        return subprocess.check_output(['whoami'], timeout=5).decode().strip()
    except:
        return "N/A"


def _fetch_hostname():
    try:
        return subprocess.check_output(['hostname'], timeout=5).decode().strip()
    except:
        return "N/A"


def _fetch_os_version():
    try:
        product = subprocess.check_output(['sw_vers', '-productVersion'], timeout=5).decode().strip()
        return f"macOS {product}"
    except:
        return "N/A"


def _fetch_kernel():
    try:
        return subprocess.check_output(['uname', '-r'], timeout=5).decode().strip()
    except:
        return "N/A"


def _fetch_uptime():
    try:
        return subprocess.check_output(['uptime'], timeout=5).decode().strip()
    except:
        return "N/A"


def _fetch_shell():
    import os
    return os.environ.get('SHELL', 'N/A').split('/')[-1]


def _fetch_terminal():
    import os
    return os.environ.get('TERM_PROGRAM', 'N/A')


def _fetch_package_count(use_macports):
    try:
        cmd = ['port', 'installed'] if use_macports else ['brew', 'list']
        result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, timeout=30).decode()
        count = len(result.strip().split('\n'))
        return str(count)
    except:
        return "N/A"


def _fetch_cpu():
    try:
        result = subprocess.check_output(['sysctl', '-n', 'machdep.cpu.brand_string'], timeout=5).decode().strip()
        return result
    except:
        return "N/A"


def _fetch_memory():
    try:
        total = subprocess.check_output(['sysctl', '-n', 'hw.memsize'], timeout=5).decode().strip()
        total_gb = int(total) / (1024**3)
        return f"{total_gb:.0f} GB"
    except:
        return "N/A"


def _fetch_disk():
    try:
        result = subprocess.check_output(['df', '-h', '/'], timeout=5).decode().strip()
        lines = result.split('\n')
        if len(lines) > 1:
            parts = lines[1].split()
            return f"{parts[2]} / {parts[1]}"
    except:
        pass
    return "N/A"


def _fetch_battery():
    try:
        result = subprocess.check_output(['pmset', '-g', 'batt'], timeout=5).decode().strip()
        for line in result.split('\n'):
            if '%' in line:
                return line.strip()
    except:
        pass
    return "N/A"


def get_all_info(use_macports=False):
    """Get all system info using parallel execution with caching."""

    def fetch_all():
        results = {}

        with ThreadPoolExecutor(max_workers=10) as executor:
            futures = {
                'User': (lambda: _cache.get('username', _fetch_username), None),
                'Hostname': (lambda: _cache.get('hostname', _fetch_hostname), None),
                'Distro': (lambda: _cache.get('os_version', _fetch_os_version), None),
                'Kernel': (lambda: _cache.get('kernel', _fetch_kernel), None),
                'Uptime': (lambda: _cache.get('uptime', _fetch_uptime), None),
                'Shell': (lambda: _cache.get('shell', _fetch_shell), None),
                'Terminal': (lambda: _cache.get('terminal', _fetch_terminal), None),
                'Packages': (lambda: _cache.get(f'packages_{use_macports}', lambda: _fetch_package_count(use_macports)), None),
                'CPU': (lambda: _cache.get('cpu', _fetch_cpu), None),
                'Memory': (lambda: _cache.get('memory', _fetch_memory), None),
                'Disk': (lambda: _cache.get('disk', _fetch_disk), None),
                'Battery': (lambda: _cache.get('battery', _fetch_battery), None),
            }

            future_to_key = {executor.submit(f[0]): k for k, f in futures.items()}

            for future in as_completed(future_to_key):
                key = future_to_key[future]
                try:
                    results[key] = future.result()
                except Exception:
                    results[key] = "N/A"

        order = ['User', 'Hostname', 'Distro', 'Kernel', 'Uptime', 'Shell',
                 'Terminal', 'Packages', 'CPU', 'Memory', 'Disk', 'Battery']
        return {k: results.get(k, "N/A") for k in order}

    return fetch_all()


def get_username():
    return _cache.get('username', _fetch_username)


def get_hostname():
    return _cache.get('hostname', _fetch_hostname)


def get_os_version():
    return _cache.get('os_version', _fetch_os_version)


def get_kernel():
    return _cache.get('kernel', _fetch_kernel)


def get_uptime():
    return _cache.get('uptime', _fetch_uptime)


def get_shell():
    return _cache.get('shell', _fetch_shell)


def get_terminal():
    return _cache.get('terminal', _fetch_terminal)


def get_package_count(use_macports=False):
    return _cache.get(f'packages_{use_macports}', lambda: _fetch_package_count(use_macports))


def get_cpu():
    return _cache.get('cpu', _fetch_cpu)


def get_memory():
    return _cache.get('memory', _fetch_memory)


def get_disk():
    return _cache.get('disk', _fetch_disk)


def get_battery():
    return _cache.get('battery', _fetch_battery)
