import os
import shutil
import socket
import subprocess
import time


BASE_DIR = os.path.dirname(os.path.dirname(__file__))
STATE_DIR = os.path.join(BASE_DIR, ".cache")
SETUP_INFO_FILE = os.path.join(STATE_DIR, "wifi_setup_mode.json")
CONNECT_RESULT_FILE = os.path.join(STATE_DIR, "wifi_connect_result.json")
CONNECTION_NAME = "its-a-plane-setup"
SSID_PREFIX = "PlaneTracker-Setup"
DEFAULT_PASSWORD = "planes12345"
SETUP_IP = "10.42.0.1"


def _run(cmd, timeout=20):
    return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)


def _nmcli_cmd(cmd):
    if shutil.which("nmcli"):
        return ["nmcli", *cmd]
    return None


def _run_nmcli(cmd, timeout=20, allow_sudo=True):
    base = _nmcli_cmd(cmd)
    if not base:
        return None
    result = _run(base, timeout=timeout)
    if result.returncode != 0 and allow_sudo and os.name != "nt" and shutil.which("sudo"):
        retry = _run(["sudo", "-n", *base], timeout=timeout)
        if retry.returncode == 0:
            return retry
    return result


def _split_nmcli(line):
    parts = []
    current = []
    escape = False
    for char in line.rstrip("\n"):
        if escape:
            current.append(char)
            escape = False
        elif char == "\\":
            escape = True
        elif char == ":":
            parts.append("".join(current))
            current = []
        else:
            current.append(char)
    parts.append("".join(current))
    return parts


def wifi_device():
    result = _run_nmcli(["-t", "-f", "DEVICE,TYPE", "device", "status"], timeout=8, allow_sudo=False)
    if result and result.returncode == 0:
        for line in result.stdout.splitlines():
            parts = _split_nmcli(line)
            if len(parts) >= 2 and parts[1] == "wifi" and not parts[0].startswith("p2p-"):
                return parts[0]
    return "wlan0"


def active_wifi_ssid():
    result = _run_nmcli(["-t", "-f", "ACTIVE,SSID", "device", "wifi", "list"], timeout=8, allow_sudo=False)
    if result and result.returncode == 0:
        for line in result.stdout.splitlines():
            parts = _split_nmcli(line)
            if len(parts) >= 2 and parts[0] == "yes":
                return parts[1]
    return ""


def setup_ssid():
    suffix = ""
    try:
        hostname = socket.gethostname().strip()
        if hostname:
            suffix = "-" + hostname[-6:].replace("_", "-")
    except Exception:
        pass
    return (SSID_PREFIX + suffix)[:32]


def is_setup_active():
    return active_wifi_ssid().startswith(SSID_PREFIX)


def _known_non_setup_connections():
    result = _run_nmcli(["-t", "-f", "NAME,TYPE,AUTOCONNECT", "connection", "show"], timeout=8, allow_sudo=False)
    if not result or result.returncode != 0:
        return []
    names = []
    for line in result.stdout.splitlines():
        parts = _split_nmcli(line)
        if len(parts) >= 2 and parts[1] == "802-11-wireless" and parts[0] != CONNECTION_NAME:
            names.append(parts[0])
    return names


def wait_for_known_wifi(seconds=35):
    deadline = time.time() + max(seconds, 0)
    while time.time() < deadline:
        ssid = active_wifi_ssid()
        if ssid and not ssid.startswith(SSID_PREFIX):
            return ssid
        time.sleep(2)
    return ""


def start_setup_ap(force=False):
    os.makedirs(STATE_DIR, exist_ok=True)
    current = active_wifi_ssid()
    if current and not current.startswith(SSID_PREFIX) and not force:
        return {"active": False, "started": False, "ssid": current, "message": f"Already connected to {current}"}

    device = wifi_device()
    ssid = setup_ssid()
    password = os.environ.get("ITS_A_PLANE_SETUP_WIFI_PASSWORD", DEFAULT_PASSWORD)

    _run_nmcli(["connection", "delete", CONNECTION_NAME], timeout=10)
    result = _run_nmcli(
        [
            "device", "wifi", "hotspot",
            "ifname", device,
            "con-name", CONNECTION_NAME,
            "ssid", ssid,
            "password", password,
        ],
        timeout=35,
    )
    ok = bool(result and result.returncode == 0)
    if ok:
        _run_nmcli(["connection", "modify", CONNECTION_NAME, "connection.autoconnect", "no"], timeout=10)

    info = {
        "active": ok,
        "started": ok,
        "ssid": ssid,
        "password": password,
        "ip": SETUP_IP,
        "url": f"http://{SETUP_IP}:8080/wifi-setup",
        "device": device,
        "message": "Setup hotspot active" if ok else ((result.stderr or result.stdout).strip() if result else "nmcli is not available"),
    }
    try:
        import json
        with open(SETUP_INFO_FILE, "w", encoding="utf-8") as f:
            json.dump(info, f, indent=2)
    except Exception:
        pass
    return info


def stop_setup_ap():
    result = _run_nmcli(["connection", "down", CONNECTION_NAME], timeout=15)
    _run_nmcli(["connection", "delete", CONNECTION_NAME], timeout=10)
    return result.returncode == 0 if result else False


def _write_connect_result(data):
    os.makedirs(STATE_DIR, exist_ok=True)
    try:
        import json
        with open(CONNECT_RESULT_FILE, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2)
    except Exception:
        pass


def _profile_exists(name):
    result = _run_nmcli(["-t", "-f", "NAME", "connection", "show"], timeout=10)
    if not result or result.returncode != 0:
        return False
    return name in [line.strip() for line in result.stdout.splitlines()]


def _delete_connection(name):
    if name:
        _run_nmcli(["connection", "delete", name], timeout=15)


def _scan_for_ssid(device, ssid, attempts=4):
    for _ in range(max(attempts, 1)):
        _run_nmcli(["device", "wifi", "rescan", "ifname", device], timeout=20)
        time.sleep(3)
        result = _run_nmcli(
            ["-t", "-f", "SSID", "device", "wifi", "list", "ifname", device],
            timeout=15,
        )
        if result and result.returncode == 0:
            for line in result.stdout.splitlines():
                if _split_nmcli(line)[0] == ssid:
                    return True
    return False


def _manual_wifi_profile(device, ssid, password):
    con_name = ssid
    _delete_connection(con_name)
    add = _run_nmcli(
        [
            "connection", "add",
            "type", "wifi",
            "ifname", device,
            "con-name", con_name,
            "ssid", ssid,
        ],
        timeout=20,
    )
    if not add or add.returncode != 0:
        return add
    if password:
        sec = _run_nmcli(
            [
                "connection", "modify", con_name,
                "wifi-sec.key-mgmt", "wpa-psk",
                "wifi-sec.psk", password,
            ],
            timeout=20,
        )
        if not sec or sec.returncode != 0:
            return sec
    else:
        _run_nmcli(["connection", "modify", con_name, "wifi-sec.key-mgmt", ""], timeout=10)
    _run_nmcli(
        [
            "connection", "modify", con_name,
            "connection.autoconnect", "yes",
            "connection.autoconnect-priority", "100",
        ],
        timeout=15,
    )
    return _run_nmcli(["connection", "up", con_name], timeout=50)


def connect_to_wifi(ssid, password=""):
    ssid = str(ssid or "").strip()
    if not ssid:
        return {"ok": False, "message": "SSID is required."}

    device = wifi_device()
    was_setup = is_setup_active()
    _write_connect_result({"ok": None, "state": "starting", "ssid": ssid})

    if was_setup:
        stop_setup_ap()
        time.sleep(5)

    _run_nmcli(["radio", "wifi", "on"], timeout=10)
    _run_nmcli(["device", "set", device, "managed", "yes"], timeout=10)
    _run_nmcli(["device", "disconnect", device], timeout=10)
    time.sleep(2)

    # Remove stale or incomplete profiles for this SSID; they can cause
    # NetworkManager errors such as "wifi-sec.key-mgmt: property is missing".
    _delete_connection(ssid)
    if _profile_exists(CONNECTION_NAME):
        _delete_connection(CONNECTION_NAME)

    _scan_for_ssid(device, ssid)

    cmd = ["device", "wifi", "connect", ssid]
    if password:
        cmd.extend(["password", password])
    if device:
        cmd.extend(["ifname", device])
    result = _run_nmcli(cmd, timeout=50)

    if not result or result.returncode != 0:
        result = _manual_wifi_profile(device, ssid, password)

    if result and result.returncode == 0:
        connection_name = ssid
        active = _run_nmcli(["-g", "GENERAL.CONNECTION", "device", "show", device], timeout=10)
        if active and active.returncode == 0 and active.stdout.strip():
            connection_name = active.stdout.strip()
        _run_nmcli(
            [
                "connection", "modify", connection_name,
                "connection.autoconnect", "yes",
                "connection.autoconnect-priority", "100",
            ],
            timeout=15,
        )
        output = {
            "ok": True,
            "state": "connected",
            "ssid": ssid,
            "connection": connection_name,
            "message": f"Connected to {ssid}.",
        }
        _write_connect_result(output)
        return output

    error = ""
    if result:
        error = (result.stderr or result.stdout).strip()
    output = {
        "ok": False,
        "state": "failed",
        "ssid": ssid,
        "message": error or f"Could not connect to {ssid}.",
    }
    _write_connect_result(output)

    if was_setup:
        time.sleep(2)
        restored = start_setup_ap(force=True)
        output["setup_restored"] = bool(restored.get("active"))
        _write_connect_result(output)
    return output


def connection_result():
    try:
        import json
        with open(CONNECT_RESULT_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return {"ok": None, "state": "idle", "message": ""}


def ensure_setup_ap_if_offline(wait_seconds=35):
    connected = wait_for_known_wifi(wait_seconds)
    if connected:
        return {"active": False, "started": False, "ssid": connected, "message": f"Connected to {connected}"}
    return start_setup_ap()


def status():
    ssid = active_wifi_ssid()
    setup = ssid.startswith(SSID_PREFIX) if ssid else False
    return {
        "active": setup,
        "ssid": ssid,
        "setup_ssid": setup_ssid(),
        "password": os.environ.get("ITS_A_PLANE_SETUP_WIFI_PASSWORD", DEFAULT_PASSWORD),
        "ip": SETUP_IP,
        "url": f"http://{SETUP_IP}:8080/wifi-setup",
        "known_connections": _known_non_setup_connections(),
    }
