#!/usr/bin/env python3
"""Generate changelogs from git commits using Ollama LLM.

Reads recent git commits, groups them via LLM into Features/Fixes/Infrastructure/Documentation,
and prepends the result to docs/CHANGELOG.md.

Cron: Monday 8am
  0 8 * * 1 cd /home/homelab/organized/repos/homelab && python3 scripts/changelog-generator.py
"""

import argparse
import logging
import sqlite3
import subprocess
import sys
from datetime import datetime, timedelta
from pathlib import Path

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")
CHANGELOG_PATH = REPO_DIR / "docs" / "CHANGELOG.md"
DB_PATH = Path(__file__).parent / "changelog.db"


def init_db(db_path: Path) -> sqlite3.Connection:
    """Initialise the SQLite DB and return a connection."""
    conn = sqlite3.connect(str(db_path))
    conn.execute(
        "CREATE TABLE IF NOT EXISTS runs "
        "(id INTEGER PRIMARY KEY AUTOINCREMENT, last_commit TEXT, run_at TEXT)"
    )
    conn.commit()
    return conn


def get_last_commit_date(conn: sqlite3.Connection) -> str | None:
    """Return the run_at timestamp of the most recent run, or None."""
    row = conn.execute("SELECT run_at FROM runs ORDER BY id DESC LIMIT 1").fetchone()
    return row[0] if row else None


def fetch_commits(since: str) -> list[str]:
    """Return git log --oneline entries since *since* (ISO date string)."""
    result = subprocess.run(
        ["git", "-C", str(REPO_DIR), "log", "--oneline", f"--since={since}", "main"],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        log.error("git log failed: %s", result.stderr.strip())
        return []
    lines = [line.strip() for line in result.stdout.strip().splitlines() if line.strip()]
    return lines


def generate_changelog(commits: list[str]) -> str:
    """Send commits to Ollama and return a formatted changelog."""
    prompt = (
        "Summarize these git commits into a changelog grouped by: "
        "Features, Fixes, Infrastructure, Documentation. "
        "Use bullet points. Be concise. Omit any empty groups.\n\n"
        + "\n".join(commits)
    )
    return ollama_generate(prompt, num_predict=1500)


def prepend_changelog(content: str, date_header: str) -> None:
    """Prepend a dated changelog entry to CHANGELOG.md."""
    CHANGELOG_PATH.parent.mkdir(parents=True, exist_ok=True)
    existing = CHANGELOG_PATH.read_text() if CHANGELOG_PATH.exists() else ""
    entry = f"## {date_header}\n\n{content}\n\n---\n\n"
    CHANGELOG_PATH.write_text(entry + existing)
    log.info("Prepended changelog entry to %s", CHANGELOG_PATH)


def record_run(conn: sqlite3.Connection, last_commit: str) -> None:
    """Record the run in the DB."""
    conn.execute(
        "INSERT INTO runs (last_commit, run_at) VALUES (?, ?)",
        (last_commit, datetime.now().isoformat()),
    )
    conn.commit()


def main() -> None:
    parser = argparse.ArgumentParser(description="Generate changelog from git commits via LLM")
    parser.add_argument("--dry-run", action="store_true", help="Print changelog without writing")
    parser.add_argument("--since", type=str, help="Override start date (YYYY-MM-DD)")
    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)

    conn = init_db(DB_PATH)

    # Determine the since date
    if args.since:
        since = args.since
    else:
        last_run = get_last_commit_date(conn)
        if last_run:
            since = last_run[:10]  # YYYY-MM-DD
        else:
            since = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d")

    log.info("Fetching commits since %s", since)
    commits = fetch_commits(since)

    if not commits:
        log.info("No new commits since %s — nothing to do", since)
        return

    log.info("Found %d commits, generating changelog...", len(commits))
    changelog = generate_changelog(commits)

    date_header = datetime.now().strftime("%Y-%m-%d")

    if args.dry_run:
        print(f"## {date_header}\n")
        print(changelog)
        return

    prepend_changelog(changelog, date_header)

    # Record the first commit hash (most recent) in the DB
    first_hash = commits[0].split()[0]
    record_run(conn, first_hash)
    log.info("Done — recorded commit %s", first_hash)


if __name__ == "__main__":
    main()
