"""VPN para clientes: cada dispositivo gera seu próprio par de chaves WireGuard.
A chave privada NUNCA sai do aparelho; o servidor só recebe a chave pública e devolve
endereço do túnel + dados públicos do servidor + chave pré-compartilhada exclusiva do dispositivo."""
import base64
import ipaddress
import os
import uuid

from fastapi import APIRouter, Depends, HTTPException

from core import NO_ID, db, decrypt, encrypt, get_current_user, now_iso
from models import VpnRegisterIn

router = APIRouter(prefix="/api/vpn")
PUBLIC_FIELDS = {"_id": 0, "id": 1, "name": 1, "country": 1, "protocol": 1, "order": 1, "endpoint": 1, "public_key": 1}


async def vpn_settings() -> dict:
    cfg = await db.config.find_one({"key": "app"}, NO_ID) or {}
    return cfg.get("vpn", {})


@router.get("/servers")
async def servers(user: dict = Depends(get_current_user)):
    out = []
    for s in await db.vpn_servers.find({"active": True}, PUBLIC_FIELDS).sort("order", 1).to_list(100):
        configured = bool(s.pop("endpoint", "") and s.pop("public_key", ""))
        s.pop("public_key", None)
        out.append({**s, "configured": configured})
    return out


@router.get("/settings")
async def settings(user: dict = Depends(get_current_user)):
    s = await vpn_settings()
    return {k: s.get(k) for k in ("enabled", "auto_reconnect", "kill_switch", "dns_leak_protection", "default_protocol")}


def allocate(pool: str, used: set[str]) -> str:
    net = ipaddress.ip_network(pool, strict=False)
    hosts = net.hosts()
    next(hosts)  # primeiro endereço reservado para o servidor
    for ip in hosts:
        if str(ip) not in used:
            return str(ip)
    raise HTTPException(409, "Sem endereços disponíveis neste servidor VPN")


@router.post("/register")
async def register(body: VpnRegisterIn, user: dict = Depends(get_current_user)):
    s = await vpn_settings()
    if not s.get("enabled", True):
        raise HTTPException(403, "VPN desativada pelo administrador")
    srv = await db.vpn_servers.find_one({"id": body.server_id, "active": True}, NO_ID)
    if not srv:
        raise HTTPException(404, "Servidor não encontrado")
    if srv["protocol"] != "wireguard":
        raise HTTPException(409, "Este servidor usa OpenVPN, ainda não suportado no app")
    if not srv.get("endpoint") or not srv.get("public_key"):
        raise HTTPException(409, "Servidor ainda não configurado pelo administrador")

    owner = await db.vpn_devices.find_one({"server_id": srv["id"], "public_key": body.public_key})
    if owner and (owner["user_id"] != user["id"] or owner["device_id"] != body.device_id):
        raise HTTPException(409, "Chave pública já registrada")

    dev = await db.vpn_devices.find_one({"user_id": user["id"], "server_id": srv["id"], "device_id": body.device_id}, NO_ID)
    if dev:
        await db.vpn_devices.update_one({"id": dev["id"]}, {"$set": {"public_key": body.public_key, "updated_at": now_iso()}})
    else:
        devices = await db.vpn_devices.distinct("device_id", {"user_id": user["id"]})
        if body.device_id not in devices and len(devices) >= int(s.get("max_devices_per_user", 3)):
            raise HTTPException(403, "Limite de dispositivos VPN atingido. Remova um dispositivo.")
        used = set(await db.vpn_devices.distinct("address", {"server_id": srv["id"]}))
        dev = {"id": str(uuid.uuid4()), "user_id": user["id"], "username": user["username"], "server_id": srv["id"],
               "device_id": body.device_id, "device_name": body.device_name, "public_key": body.public_key,
               "address": allocate(srv["address_pool"], used),
               "psk_enc": encrypt(base64.b64encode(os.urandom(32)).decode()), "created_at": now_iso()}
        await db.vpn_devices.insert_one(dict(dev))

    dns = srv.get("dns") or s.get("dns_servers") or []
    return {
        "device_id": body.device_id,
        "interface": {"address": f"{dev['address']}/32", "dns": dns, "mtu": srv.get("mtu")},
        "peer": {"public_key": srv["public_key"], "preshared_key": decrypt(dev["psk_enc"]),
                 "endpoint": f"{srv['endpoint']}:{srv['port']}", "allowed_ips": srv["allowed_ips"],
                 "persistent_keepalive": srv["persistent_keepalive"]},
        "kill_switch": s.get("kill_switch", True), "dns_leak_protection": s.get("dns_leak_protection", True),
    }


@router.get("/devices")
async def my_devices(user: dict = Depends(get_current_user)):
    return await db.vpn_devices.find({"user_id": user["id"]}, {"_id": 0, "psk_enc": 0}).to_list(50)


@router.delete("/devices/{did}")
async def remove_device(did: str, user: dict = Depends(get_current_user)):
    res = await db.vpn_devices.delete_one({"id": did, "user_id": user["id"]})
    if not res.deleted_count:
        raise HTTPException(404, "Dispositivo não encontrado")
    return {"ok": True}
