import json
import datetime
from django.utils import timezone
from router_manager.models import RouterInformation, Router
from routerlib.functions import connect_to_ssh


def _parse_routeros_key_value_output(output: str) -> dict:
    """
    Parse lines like "key:    value" into a dict.
    Skip blank lines or lines starting with '[' (the prompt).
    """
    data = {}
    # Normalize and split
    for raw_line in output.replace('\r', '').splitlines():
        line = raw_line.strip()
        # skip empty or prompt lines
        if not line or line.startswith('['):
            continue
        if ':' not in line:
            continue
        key, val = line.split(':', 1)
        data[key.strip()] = val.strip()
    return data


def get_router_information(router_information: RouterInformation):
    """
    Connect to the router, retrieve info, and store it in RouterInformation.
    """
    router = router_information.router
    field_max_length = 100
    success = False
    error_message = ''

    try:
        ssh = connect_to_ssh(router.address, router.port, router.username, router.password, router.ssh_key)
        json_data = {}

        if router.router_type in ('routeros', 'routeros-branded'):
            for cmd in ['/system resource print', '/system routerboard print']:
                stdin, stdout, stderr = ssh.exec_command(cmd)
                raw = stdout.read().decode('utf-8', errors='ignore')
                parsed = _parse_routeros_key_value_output(raw)
                json_data[cmd] = parsed

            rb = json_data['/system routerboard print']
            sr = json_data['/system resource print']

            if sr:
                router_information.model_name = sr.get('board-name', '')[:field_max_length]
                router_information.os_version = sr.get('version', '')[:field_max_length]
                router_information.architecture = sr.get('architecture-name', '')[:field_max_length]
                router_information.cpu = sr.get('cpu', '')[:field_max_length]
                success = True
            if rb:
                router_information.model_version = rb.get('model', '')[:field_max_length]
                router_information.serial_number = rb.get('serial-number', '')[:field_max_length]
                router_information.firmware_version = rb.get('current-firmware', '')[:field_max_length]
                success = True
            if not success:
                return False, 'Failed to retrieve router information'

        elif router.router_type == 'openwrt':
            stdin, stdout, stderr = ssh.exec_command('cat /etc/os-release')
            osrel = {}
            for line in stdout.read().decode('utf-8').splitlines():
                if '=' in line:
                    k, v = line.split('=', 1)
                    osrel[k] = v.strip().strip('"')
            json_data['cat /etc/os-release'] = osrel

            # hostname
            stdin, stdout, stderr = ssh.exec_command('uci get system.@system[0].hostname')
            hostname = stdout.read().decode('utf-8').strip()
            json_data['uci get system.@system[0].hostname'] = hostname

            # architecture
            stdin, stdout, stderr = ssh.exec_command('uname -m')
            arch = stdout.read().decode('utf-8').strip()
            json_data['uname -m'] = arch

            # fallback serial (MAC of eth0)
            stdin, stdout, stderr = ssh.exec_command('cat /sys/class/net/eth0/address')
            mac = stdout.read().decode('utf-8').strip()
            json_data['cat /sys/class/net/eth0/address'] = mac

            if osrel:
                router_information.model_name       = osrel.get('OPENWRT_DEVICE_MODEL', '')[:field_max_length]
                router_information.model_version    = osrel.get('VERSION_ID', '')[:field_max_length]
                router_information.serial_number    = mac[:field_max_length]
                router_information.os_version       = osrel.get('VERSION', '')[:field_max_length]
                router_information.firmware_version = osrel.get('OPENWRT_RELEASE', '')[:field_max_length]
                router_information.architecture     = arch[:field_max_length]
                success = True
            if not success:
                return False, 'Failed to retrieve router information'
        else:
            return False, f"Router type not supported: {router.get_router_type_display()}"

        if success:
            router_information.success        = True
            router_information.error          = False
            router_information.retry_count    = 0
            router_information.next_retry     = None
            router_information.error_message = ''
            router_information.last_retrieval = timezone.now()
            router_information.json_data = json.dumps(json_data)
            router_information.save()

    except Exception as e:
        success = False
        error_message = str(e)

    finally:
        try:
            ssh.close()
        except:
            pass

    return success, error_message


def update_router_information(router_information: RouterInformation):
    max_retry = 3
    retry_minutes = 5

    success = False
    error_message = ''

    if router_information.retry_count > max_retry:
        router_information.error = True
        router_information.success = False
        router_information.next_retry = None
        router_information.retry_count = 0
        router_information.last_retrieval = timezone.now()
        if router_information.error_message:
            router_information.error_message += f"\nMax retries reached for {router_information.router.name}"
        else:
            router_information.error_message = f"Max retries reached for {router_information.router.name}"
        router_information.save()
        return False, router_information.error_message
    try:
        success, error_message = get_router_information(router_information)
    except Exception as e:
        success = False
        error_message = f"Failed to update router information for {router_information.router.name}. Exception: {e}"

    if not success:
        router_information.error = True
        router_information.success = False
        router_information.next_retry = timezone.now() + datetime.timedelta(minutes=retry_minutes)
        router_information.retry_count += 1
        router_information.last_retrieval = timezone.now()
        if error_message:
            router_information.error_message = error_message
        else:
            router_information.error_message = f"Failed to update router information for {router_information.router.name}"
        router_information.save()

    return success, error_message
