# src/komet/main.py

import os
import sys
from pathlib import Path

import git                         # GitPython library to interact with git
from groq import Groq               # Official Groq SDK for calling their API
import typer                        # Modern, beautiful CLI framework (like Click but nicer)

# Create the main Typer application
# name="komet"   → the command will be called "komet"
# help=...       → shown when user runs: komet --help
app = typer.Typer(
    name="komet",
    help="Generate a Conventional Commit message from staged changes using Groq LLM",
    add_completion=False,           # disables shell completion (we don't need it yet)
)


def get_staged_diff() -> str:
    """
    Read the staged changes (what `git diff --cached` would show).
    Returns the diff as string or exits with error message.
    """
    # Try to open the current directory (or parent) as git repo
    try:
        repo = git.Repo(Path.cwd(), search_parent_directories=True)
    except git.InvalidGitRepositoryError:
        typer.secho("Error: Not inside a git repository.", fg=typer.colors.RED, err=True)
        raise typer.Exit(code=1)

    # Check if there are actually staged changes
    if not repo.is_dirty(index=True):           # index=True → checks staged area
        typer.secho("No staged changes found.", fg=typer.colors.YELLOW, err=True)
        raise typer.Exit(code=0)

    # Get the actual diff of staged files
    diff = repo.git.diff("--cached")            # same as: git diff --cached

    # Safety: if somehow diff is empty
    if not diff.strip():
        typer.secho("Staged changes are empty.", fg=typer.colors.YELLOW, err=True)
        raise typer.Exit(code=0)

    # Optional: prevent sending huge diffs (Groq has large context, but still good practice)
    MAX_DIFF_CHARS = 15000
    if len(diff) > MAX_DIFF_CHARS:
        diff = diff[:MAX_DIFF_CHARS] + "\n\n# ... (diff was truncated to fit context)"

    return diff


def generate_commit_message(diff_text: str, api_key: str) -> str:
    """
    Send the diff to Groq LLM and ask it to create a Conventional Commit message.
    Returns only the generated message string.
    """
    client = Groq(api_key=api_key)

    # This is the most important part — controls quality a lot
    system_prompt = """You are a senior software engineer who writes **perfect, team-friendly Conventional Commits** every time.

MANDATORY RULES — follow exactly or the output is invalid:

1. Type must be one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
2. Add scope in (parentheses) when it adds clarity (db, api, auth, ui, cli, config, tests, …)
3. Subject line: imperative mood ("add feature", "fix bug", "refactor class") — NEVER past tense
4. Subject line length: maximum 72 characters (including type and scope)
5. No period at the end of the subject line
6. If the change affects public API / breaks compatibility → add footer: BREAKING CHANGE: one sentence explanation
7. Optional body: after one blank line — explain WHAT changed and WHY (keep under 4 lines)
8. Use bullet points in body only if it really helps readability
9. Never include:
   - Emojis
   - Markdown formatting
   - ``` fences
   - Explanations outside the message
   - "Here is the commit message:"
10. Output **ONLY** the final commit message — nothing else

Few perfect examples:

feat: add password reset flow
fix: correct JWT token validation on refresh

docs(readme): update installation instructions with poetry commands

refactor(cli): extract diff parsing to separate function
    - Improves testability
    - Reduces main.py complexity

BREAKING CHANGE: removed deprecated --old-flag option

Your turn — analyze the diff and create the message.
"""

    user_prompt = f"""Analyze these staged code changes and write the commit message:

{diff_text}
"""

    try:
        response = client.chat.completions.create(
            model="llama-3.3-70b-versatile",          # good balance of quality & speed on Groq
            # model="mixtral-8x7b-32768",             # cheaper & still very good
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user",   "content": user_prompt},
            ],
            temperature=0.15,           # low = more consistent & deterministic
            max_tokens=180,             # enough for message + body
            top_p=0.9,
        )

        message = response.choices[0].message.content.strip()
        return message

    except Exception as e:
        typer.secho(f"Groq API error: {str(e)}", fg=typer.colors.RED, err=True)
        raise typer.Exit(code=1)


@app.command()
def main():
    """
    Main command — called when user just types: komet
    """
    # Get Groq API key from environment (most secure & common way)
    api_key = os.getenv("GROQ_API_KEY")
    if not api_key:
        typer.secho(
            "Error: GROQ_API_KEY environment variable is not set.\n"
            "Get your key at: https://console.groq.com/keys\n"
            "Then run: export GROQ_API_KEY=gq_xxxxxxxxxxxxxxxx",
            fg=typer.colors.RED,
            err=True
        )
        raise typer.Exit(code=1)

    # 1. Read staged changes
    diff = get_staged_diff()

    # 2. Ask LLM to generate message
    message = generate_commit_message(diff, api_key)

    # 3. Print nicely
    typer.echo("\nSuggested commit message:\n")
    typer.secho(message, fg=typer.colors.GREEN, bold=True)
    typer.echo("\nUse it like this:")
    typer.echo(f"  git commit -m '{message.splitlines()[0]}'")
    # If message has body → show full multi-line version
    if "\n" in message:
        typer.echo("\nFull version:")
        typer.echo(message)


if __name__ == "__main__":
    # This makes the script runnable directly: python -m komet
    # But normally you'll run it via poetry run komet or after pipx install
    app()