#!/usr/bin/env python3
"""Generate Ansible playbooks from plain English descriptions using Ollama LLM.

Usage:
  python3 scripts/ansible-generator.py "Install and configure nginx on all debian hosts"
  python3 scripts/ansible-generator.py --name nginx-setup "Install nginx on debian hosts"
  python3 scripts/ansible-generator.py --dry-run "Restart Docker on atlantis and calypso"
"""

import argparse
import logging
import re
import sys
from pathlib import Path

import yaml

sys.path.insert(0, str(Path(__file__).parent))
from lib.ollama import ollama_generate, ollama_available, OllamaUnavailableError

log = logging.getLogger(__name__)

REPO_DIR = Path("/home/homelab/organized/repos/homelab")
PLAYBOOKS_DIR = REPO_DIR / "ansible" / "playbooks"
GENERATED_DIR = PLAYBOOKS_DIR / "generated"
INVENTORY_PATH = REPO_DIR / "ansible" / "inventory.yml"

# Reference playbooks for style
STYLE_REFS = [
    PLAYBOOKS_DIR / "deploy_atlantis.yml",
    PLAYBOOKS_DIR / "update_system.yml",
]


def load_style_examples() -> str:
    """Load first 50 lines of reference playbooks for style guidance."""
    examples = []
    for ref in STYLE_REFS:
        if ref.exists():
            lines = ref.read_text().splitlines()[:50]
            examples.append(f"# {ref.name}\n" + "\n".join(lines))
    return "\n\n".join(examples)


def load_inventory() -> str:
    if INVENTORY_PATH.exists():
        return INVENTORY_PATH.read_text()
    return "(inventory not found)"


def slugify(text: str) -> str:
    """Convert description to a filename-safe slug."""
    slug = re.sub(r"[^a-z0-9]+", "-", text.lower().strip())
    slug = slug.strip("-")
    return slug[:60] if slug else "playbook"


def generate_playbook(description: str) -> str:
    examples = load_style_examples()
    inventory = load_inventory()

    prompt = (
        f"Generate an Ansible playbook for: {description}\n\n"
        f"Follow this style:\n{examples}\n\n"
        f"Use this inventory:\n{inventory}\n\n"
        "Output valid YAML only. Include proper hosts, tasks, handlers, and tags.\n"
        "Do not wrap the YAML in code fences. Output only the raw YAML document starting with ---."
    )
    return ollama_generate(prompt, num_predict=3000, timeout=180)


def extract_yaml(raw: str) -> str:
    """Strip any markdown code fences the LLM may have added."""
    # Remove ```yaml ... ``` wrapping
    raw = re.sub(r"^```(?:ya?ml)?\s*\n", "", raw.strip())
    raw = re.sub(r"\n```\s*$", "", raw.strip())
    return raw.strip()


def validate_yaml(content: str) -> bool:
    """Check that content is valid YAML."""
    try:
        yaml.safe_load(content)
        return True
    except yaml.YAMLError as e:
        log.error("Generated YAML is invalid: %s", e)
        return False


def main() -> None:
    parser = argparse.ArgumentParser(description="Generate Ansible playbooks from plain English via LLM")
    parser.add_argument("description", help="Plain English description of the desired playbook")
    parser.add_argument("--name", type=str, help="Output filename (without .yml extension)")
    parser.add_argument("--dry-run", action="store_true", help="Print playbook without saving")
    parser.add_argument("--verbose", "-v", action="store_true", help="Enable debug logging")
    args = parser.parse_args()

    logging.basicConfig(
        level=logging.DEBUG if args.verbose else logging.INFO,
        format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    )

    if not ollama_available():
        log.error("Ollama is not reachable — aborting")
        sys.exit(1)

    log.info("Generating playbook for: %s", args.description)
    raw = generate_playbook(args.description)
    playbook_yaml = extract_yaml(raw)

    if not validate_yaml(playbook_yaml):
        log.error("LLM output failed YAML validation. Raw output:\n%s", raw)
        sys.exit(1)

    if args.dry_run:
        print(playbook_yaml)
        return

    filename = args.name if args.name else slugify(args.description)
    if not filename.endswith(".yml"):
        filename += ".yml"

    GENERATED_DIR.mkdir(parents=True, exist_ok=True)
    out_path = GENERATED_DIR / filename
    out_path.write_text(playbook_yaml + "\n")
    log.info("Saved playbook to %s", out_path)


if __name__ == "__main__":
    main()
