"""Service snapshot QuickWin — prise de snapshots VM via vSphere/pyvmomi
Ordre de recherche des VM sur les vCenters:
  - Hors-prod: Senlis (vpgesavcs1) → Nanterre (vpmetavcs1) → DR (vpsicavcs1)
  - Prod:      Nanterre (vpmetavcs1) → Senlis (vpgesavcs1) → DR (vpsicavcs1)
Physiques: pas de snapshot, alerte Commvault."""
import ssl
import logging
from datetime import datetime

log = logging.getLogger("quickwin.snapshot")

try:
    from pyVim.connect import SmartConnect, Disconnect
    from pyVmomi import vim
    PYVMOMI_OK = True
except ImportError:
    PYVMOMI_OK = False
    log.warning("pyvmomi non disponible — snapshots impossibles")


def _get_secret(db, key):
    try:
        from ..services.secrets_service import get_secret
        return get_secret(db, key)
    except Exception:
        return None


def _connect_vcenter(endpoint, user, password):
    """Connexion a un vCenter. Retourne un ServiceInstance ou None."""
    try:
        ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE
        si = SmartConnect(host=endpoint, user=user, pwd=password, sslContext=ctx)
        return si
    except Exception as e:
        log.warning(f"Connexion vCenter {endpoint} echouee: {e}")
        return None


def _find_vm(si, vm_name):
    """Cherche une VM par nom dans le vCenter.
       Matching tolérant :
         1. nom exact (insensible à la casse)
         2. partie courte avant le 1er '.' (ex: 'vrexpbtex1' == 'vrexpbtex1.sanef.groupe')
       Logue un échantillon de VMs vues pour faciliter le debug en cas d'échec.
    """
    target_full = (vm_name or "").lower().strip()
    short = target_full.split(".")[0]
    if not short:
        return None

    content = si.RetrieveContent()
    container = content.viewManager.CreateContainerView(
        content.rootFolder, [vim.VirtualMachine], True)
    total = 0
    exact_match = None
    short_match = None
    samples = []
    try:
        for vm in container.view:
            total += 1
            if not vm.name:
                continue
            n = vm.name.lower().strip()
            if exact_match is None and n == target_full:
                exact_match = vm
            elif short_match is None and n.split(".")[0] == short:
                short_match = vm
            if len(samples) < 5 and n.startswith(short[:3]):
                samples.append(vm.name)
    finally:
        container.Destroy()

    found = exact_match or short_match
    if found is None:
        log.info(f"_find_vm({vm_name}): no match parmi {total} VMs ; "
                 f"samples startswith '{short[:3]}': {samples}")
    else:
        kind = "exact" if exact_match else "short"
        log.info(f"_find_vm({vm_name}): {kind} match → {found.name}")
    return found


def _take_snapshot(vm, snap_name, description=""):
    """Prend un snapshot de la VM. Retourne (ok, message)."""
    try:
        task = vm.CreateSnapshot_Task(
            name=snap_name,
            description=description,
            memory=False,
            quiesce=True,
        )
        # Attendre la fin du task
        while task.info.state in (vim.TaskInfo.State.queued, vim.TaskInfo.State.running):
            import time
            time.sleep(2)
        if task.info.state == vim.TaskInfo.State.success:
            return True, "Snapshot OK"
        else:
            err = str(task.info.error) if task.info.error else "Echec inconnu"
            return False, f"Snapshot echoue: {err}"
    except Exception as e:
        return False, f"Erreur snapshot: {e}"


def get_vcenter_order(db, branch):
    """Retourne la liste ordonnee des vCenters selon la branche.
       Endpoints reconnus :
         - gestion : vpgesavcs1(.sanef.groupe)  (= Senlis)
         - metier  : vpmetavcs1(.sanef.groupe)  (= Nanterre)
         - DR/SIA  : vpsiaavcs1(.sanef.groupe)  (fallback ultime)
       Matching tolérant par endpoint OU par name de la table vcenters.
       Ordre :
         prod  : metier  → gestion → dr
         hprod : gestion → metier  → dr"""
    from sqlalchemy import text
    vcenters = db.execute(text(
        "SELECT id, name, endpoint FROM vcenters WHERE is_active = true ORDER BY id"
    )).fetchall()

    vc_map = {}
    for vc in vcenters:
        ep = (vc.endpoint or "").lower()
        nm = (vc.name or "").lower()
        if "vpgesavcs1" in ep or "gestion" in nm:
            vc_map["gestion"] = vc
        elif "vpmetavcs1" in ep or "metier" in nm:
            vc_map["metier"] = vc
        elif "vpsiaavcs1" in ep or "vpsicavcs1" in ep or "sia" in nm or nm == "dr":
            vc_map["dr"] = vc
        else:
            vc_map.setdefault("other", []).append(vc)

    if branch == "prod":
        order = [vc_map.get("metier"), vc_map.get("gestion"), vc_map.get("dr")]
    else:
        order = [vc_map.get("gestion"), vc_map.get("metier"), vc_map.get("dr")]

    return [v for v in order if v is not None]


def snapshot_server(hostname, vm_name, branch, db, snap_name=None):
    """Prend un snapshot pour un serveur.
    Cherche la VM sur les vCenters dans l'ordre selon la branche.
    Retourne dict: {ok, vcenter, detail, skipped}"""
    if not PYVMOMI_OK:
        return {"ok": False, "vcenter": "", "detail": "pyvmomi non installe", "skipped": True}

    # Fallback historique : vcenter_user/vcenter_pass (legacy) ou vsphere_user/vsphere_pass (Settings UI)
    # .strip() défensif : un copier-coller peut introduire \n / \r / espaces
    vc_user = (_get_secret(db, "vcenter_user") or _get_secret(db, "vsphere_user") or "").strip()
    vc_pass = (_get_secret(db, "vcenter_pass") or _get_secret(db, "vsphere_pass") or "").strip()
    if not vc_user or not vc_pass:
        return {"ok": False, "vcenter": "",
                "detail": "Credentials vCenter manquants (renseigner vsphere_user/vsphere_pass dans Settings > vSphere)",
                "skipped": True}

    search_name = vm_name or hostname
    if not snap_name:
        snap_name = f"QW_{datetime.now().strftime('%Y%m%d_%H%M')}"

    vcenters = get_vcenter_order(db, branch)
    if not vcenters:
        return {"ok": False, "vcenter": "", "detail": "Aucun vCenter actif configure", "skipped": True}

    login_failures = []
    not_found_on = []
    for vc in vcenters:
        si = _connect_vcenter(vc.endpoint, vc_user, vc_pass)
        if not si:
            login_failures.append(vc.name)
            continue
        try:
            vm = _find_vm(si, search_name)
            if vm:
                ok, msg = _take_snapshot(vm, snap_name,
                                         description=f"PatchCenter snapshot {hostname}")
                return {"ok": ok, "vcenter": vc.name, "detail": msg}
            not_found_on.append(vc.name)
        finally:
            try:
                Disconnect(si)
            except Exception:
                pass

    # Tous les vCenters épuisés sans match → message précis
    if login_failures and not not_found_on:
        return {
            "ok": False, "vcenter": "",
            "detail": (f"Échec login sur tous les vCenters ({', '.join(login_failures)}). "
                       "Vérifier vsphere_user/vsphere_pass dans Settings > vSphere "
                       "(format attendu : user@vsphere.local ou DOMAIN\\\\user)."),
            "skipped": True,
        }
    if login_failures:
        return {
            "ok": False, "vcenter": "",
            "detail": (f"VM '{search_name}' non trouvée sur {', '.join(not_found_on)} ; "
                       f"login KO sur {', '.join(login_failures)}"),
        }
    return {"ok": False, "vcenter": "",
            "detail": f"VM '{search_name}' non trouvée sur : {', '.join(not_found_on)}"}
