#!/usr/bin/env python3
"""
aibv_scraper.py
Scrape les RDV CT existants sur planning.aibv.be et met à jour la colonne
rdv_ct de la table vehicles en DB Fleet2Cube.

Dépendances : pip install requests beautifulsoup4 pymysql
Cron OVH    : python3 /home/fleetal/www/aibv_scraper.py
"""

import re
import sys
from datetime import datetime

import requests
from bs4 import BeautifulSoup
import pymysql

# ── Configuration ──────────────────────────────────────────────────────────────
AIBV_LOGIN_URL = "https://planning.aibv.be/Login.aspx"
AIBV_RDV_URL   = "https://planning.aibv.be/Reservaties/ReservatieOverzicht.aspx"
AIBV_USER      = "n.goossens@monnaie.be"
AIBV_PASS      = "Monnaie2024"

DB_HOST = "gn61740-001.eu.clouddb.ovh.net"
DB_PORT = 35104
DB_NAME = "DataBase"
DB_USER = "Admin"
DB_PASS = "Nicka1676"

# ── Utilitaires ────────────────────────────────────────────────────────────────

def normalize_plate(plate: str) -> str:
    return plate.strip().upper().replace(" ", "").replace("-", "")

def parse_date(text: str):
    """Extrait une date DD/MM/YYYY depuis un texte."""
    text = text.strip()
    # Cherche DD/MM/YYYY ou D/M/YYYY
    m = re.search(r'\b(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})\b', text)
    if m:
        try:
            return datetime(int(m.group(3)), int(m.group(2)), int(m.group(1))).date()
        except ValueError:
            pass
    # Cherche YYYY-MM-DD
    m = re.search(r'\b(\d{4})-(\d{2})-(\d{2})\b', text)
    if m:
        try:
            return datetime(int(m.group(1)), int(m.group(2)), int(m.group(3))).date()
        except ValueError:
            pass
    return None

def get_hidden_fields(soup) -> dict:
    """Récupère tous les champs hidden d'un formulaire ASP.NET."""
    fields = {}
    for inp in soup.find_all("input", {"type": "hidden"}):
        name = inp.get("name")
        if name:
            fields[name] = inp.get("value", "")
    return fields

# ── Scraping AIBV ──────────────────────────────────────────────────────────────

def scrape_reservations() -> dict:
    """
    Retourne {plaque_normalisee: date_rdv (date)}.
    """
    session = requests.Session()
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/120.0.0.0 Safari/537.36",
        "Accept-Language": "fr-FR,fr;q=0.9",
    })

    # ── 1. GET page de login → récupérer ViewState + champs hidden ────────────
    print("→ Chargement page de login...")
    resp = session.get(AIBV_LOGIN_URL, timeout=20)
    resp.raise_for_status()
    soup = BeautifulSoup(resp.text, "html.parser")

    payload = get_hidden_fields(soup)

    # Trouver dynamiquement les noms des champs username / password / submit
    user_input = soup.find("input", {"id": re.compile(r"UserName", re.I)}) \
              or soup.find("input", {"name": re.compile(r"UserName", re.I)}) \
              or soup.find("input", {"type": "email"})
    pass_input = soup.find("input", {"type": "password"})
    submit_btn = soup.find("input", {"type": "submit"}) \
              or soup.find("button", {"type": "submit"})

    if not user_input or not pass_input:
        raise RuntimeError("Champs de login introuvables sur la page.")

    payload[user_input["name"]] = AIBV_USER
    payload[pass_input["name"]] = AIBV_PASS
    if submit_btn and submit_btn.get("name"):
        payload[submit_btn["name"]] = submit_btn.get("value", "Login")

    # ── 2. POST login ──────────────────────────────────────────────────────────
    print("→ Envoi des identifiants...")
    resp = session.post(AIBV_LOGIN_URL, data=payload, timeout=20, allow_redirects=True)
    resp.raise_for_status()

    # Vérifier qu'on est bien connecté (pas revenu sur la page de login)
    if "Login.aspx" in resp.url and "password" in resp.text.lower():
        raise RuntimeError("Échec du login AIBV — vérifiez les identifiants.")
    print("✓ Connecté !")

    # ── 3. GET page des réservations ──────────────────────────────────────────
    print("→ Chargement des réservations...")
    resp = session.get(AIBV_RDV_URL, timeout=20)
    resp.raise_for_status()
    soup = BeautifulSoup(resp.text, "html.parser")

    reservations = {}

    # ── 4. Extraction plaques + dates ─────────────────────────────────────────
    # Stratégie 1 : spans avec id contenant 'Inschrijvingsplaat' (plaque)
    plate_spans = soup.find_all("span", {"id": re.compile(r"Inschrijvingsplaat|Plaat", re.I)})

    if plate_spans:
        print(f"  {len(plate_spans)} plaque(s) trouvée(s) via spans dédiés.")
        for span in plate_spans:
            plate_raw = span.get_text(strip=True)
            if not plate_raw:
                continue
            plate_norm = normalize_plate(plate_raw)

            # Chercher la date dans la même <tr>
            row = span.find_parent("tr")
            if not row:
                continue

            # Spans de date dans la ligne
            date_span = row.find("span", {"id": re.compile(r"Datum|Date|datum|Tijdstip", re.I)})
            date_obj = None
            if date_span:
                date_obj = parse_date(date_span.get_text())
            if not date_obj:
                # Fallback : chercher une date dans le texte de la ligne entière
                date_obj = parse_date(row.get_text())

            if date_obj:
                reservations[plate_norm] = date_obj
                print(f"  ✓ {plate_raw} → {date_obj.strftime('%d/%m/%Y')}")
            else:
                print(f"  ⚠ {plate_raw} → date non trouvée")

    else:
        # Stratégie 2 : parcourir toutes les lignes de toutes les tables
        print("  Spans dédiés non trouvés — analyse des tables...")
        for table in soup.find_all("table"):
            for row in table.find_all("tr"):
                cells = [td.get_text(strip=True) for td in row.find_all("td")]
                if len(cells) < 2:
                    continue
                row_text = " ".join(cells)

                # Cherche plaque belge : 1-KPB-990, 2-CGP-516, etc.
                plate_m = re.search(
                    r'\b(\d[-\s]?[A-Z]{2,3}[-\s]?\d{2,3})\b',
                    row_text.upper()
                )
                date_obj = parse_date(row_text)

                if plate_m and date_obj:
                    plate_norm = normalize_plate(plate_m.group(1))
                    reservations[plate_norm] = date_obj
                    print(f"  ✓ {plate_m.group(1)} → {date_obj.strftime('%d/%m/%Y')}")

    if not reservations:
        print("  ⚠ Aucune réservation trouvée sur la page.")
        # Debug : afficher un extrait du HTML pour diagnostic
        print("  [DEBUG] Extrait HTML :")
        print(soup.get_text()[:1000])

    return reservations

# ── Mise à jour DB ─────────────────────────────────────────────────────────────

def update_db(reservations: dict):
    conn = pymysql.connect(
        host=DB_HOST, port=DB_PORT, db=DB_NAME,
        user=DB_USER, passwd=DB_PASS,
        charset="utf8mb4", connect_timeout=10
    )
    cur = conn.cursor()

    # Créer colonne rdv_ct si inexistante
    try:
        cur.execute("ALTER TABLE vehicles ADD COLUMN rdv_ct DATE NULL DEFAULT NULL")
        conn.commit()
        print("✓ Colonne rdv_ct créée.")
    except Exception:
        pass  # existe déjà

    # Remettre tout à NULL (supprimer les RDV qui n'existent plus sur AIBV)
    cur.execute("UPDATE vehicles SET rdv_ct = NULL")

    updated = 0
    not_found = []
    for plate_norm, date_obj in reservations.items():
        cur.execute(
            "SELECT vehicle_id, plate FROM vehicles "
            "WHERE UPPER(REPLACE(REPLACE(plate, ' ', ''), '-', '')) = %s",
            (plate_norm,)
        )
        row = cur.fetchone()
        if row:
            cur.execute(
                "UPDATE vehicles SET rdv_ct = %s WHERE vehicle_id = %s",
                (date_obj.strftime("%Y-%m-%d"), row[0])
            )
            print(f"  ✓ {row[1]} → rdv_ct = {date_obj.strftime('%d/%m/%Y')}")
            updated += 1
        else:
            not_found.append(plate_norm)

    conn.commit()
    cur.close()
    conn.close()

    print(f"\n✓ {updated} véhicule(s) mis à jour.")
    if not_found:
        print(f"⚠ Plaques non trouvées en DB : {', '.join(not_found)}")

# ── Main ───────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("═══ Sync AIBV → Fleet2Cube ═══\n")
    try:
        rdvs = scrape_reservations()
        print(f"\n→ {len(rdvs)} réservation(s) récupérée(s).\n")
        update_db(rdvs)
        print("\n✓ Terminé.")
    except Exception as e:
        print(f"\n✗ Erreur : {e}")
        sys.exit(1)
