#!/usr/bin/env python3
"""
Export un checkpoint LoRA en GGUF pour Ollama.

Stratégie : charge le modèle de base via Unsloth (pour bénéficier de
save_pretrained_gguf), puis applique le LoRA depuis le checkpoint
via PeftModel.from_pretrained en copiant la méthode GGUF.

Usage: python scripts/export_checkpoint_gguf.py [--checkpoint models/pmsi-lora-checkpoints/checkpoint-7000]
"""

import argparse
from pathlib import Path

BASE = Path(__file__).resolve().parent.parent
OUTPUT = BASE / "models"


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--checkpoint", default=str(OUTPUT / "pmsi-lora-checkpoints" / "checkpoint-7000"))
    parser.add_argument("--model", default="unsloth/gemma-3-12b-it-bnb-4bit")
    parser.add_argument("--max-seq-length", type=int, default=512)
    parser.add_argument("--quant", default="q4_k_m")
    args = parser.parse_args()

    from unsloth import FastLanguageModel
    from peft import PeftModel

    checkpoint_dir = Path(args.checkpoint)
    gguf_dir = OUTPUT / "pmsi-gguf-v2"
    gguf_dir.mkdir(parents=True, exist_ok=True)

    # Étape 1 : Charger base via Unsloth (installe save_pretrained_gguf)
    print(f"[1/3] Chargement du modèle de base via Unsloth...")
    model, tokenizer = FastLanguageModel.from_pretrained(
        model_name=args.model,
        max_seq_length=args.max_seq_length,
        dtype=None,
        load_in_4bit=True,
    )

    # Sauver la méthode GGUF avant que PeftModel ne l'écrase
    save_gguf_fn = model.save_pretrained_gguf

    # Étape 2 : Appliquer le LoRA checkpoint
    print(f"[2/3] Application LoRA depuis {checkpoint_dir.name}...")
    model = PeftModel.from_pretrained(model, str(checkpoint_dir))

    # Réattacher la méthode GGUF d'Unsloth au modèle PeftModel
    model.save_pretrained_gguf = save_gguf_fn.__func__.__get__(model, type(model))

    # Vérifier que les poids LoRA sont bien chargés
    lora_params = [n for n, p in model.named_parameters() if "lora" in n and p.requires_grad]
    print(f"  Paramètres LoRA actifs : {len(lora_params)}")

    # Étape 3 : Export GGUF
    print(f"[3/3] Export GGUF ({args.quant})...")
    model.save_pretrained_gguf(
        str(gguf_dir),
        tokenizer,
        quantization_method=args.quant,
    )

    # Résultat
    gguf_files = sorted(gguf_dir.glob("*.gguf"), key=lambda f: f.stat().st_size)
    if not gguf_files:
        print("Aucun GGUF produit !")
        return

    final_gguf = gguf_files[0]
    for g in gguf_files:
        size_gb = g.stat().st_size / 1024**3
        print(f"  {g.name} ({size_gb:.1f} Go)")

    # Modelfile
    modelfile_path = gguf_dir / "Modelfile"
    with open(modelfile_path, "w") as f:
        f.write(f"FROM {final_gguf.name}\n\n")
        f.write("PARAMETER temperature 0.3\n")
        f.write("PARAMETER top_p 0.9\n")
        f.write("PARAMETER num_ctx 8192\n")

    print(f"\nTerminé !")
    print(f"  GGUF : {final_gguf}")
    print(f"\nPour importer dans Ollama :")
    print(f"  cd {gguf_dir}")
    print(f"  ollama create pmsi-coder -f Modelfile")


if __name__ == "__main__":
    main()
