import os
import sys
import json
import re
import requests

from .utils import run_command, sanitize_text


PROMPT_TEMPLATE = """You are an expert in writing Git commit messages that strictly adhere to the Conventional Commits 1.0.0 specification, with additional opinionated rules. Below is a diff of staged changes from the command:

```
git diff --cached
```

```
{diff_content}
```

Generate a commit message with the following structure:
<type>([scope]): <description>
[optional body]
[optional footer(s)]

### Requirements:
- **Type**: Use '{commit_type}' if provided; otherwise, infer from the diff. Valid types: feat, fix, refactor, style, test, docs, build, ops, chore, revert. Note: 'perf' is a special 'refactor' type for performance improvements.
- **Scope**: Use '{scope}' if provided; otherwise, infer a concise scope (max 20 characters, e.g., module or component name) or omit if not applicable. Enclose in parentheses, e.g., (api). Do not use issue identifiers as scopes.
- **Description**: A concise (max 72 characters), clear summary in imperative, present tense (e.g., 'add', 'fix', 'update'). Use lowercase first letter, no period at the end.
- **Body**: Optional. Include only if needed for motivation or context, starting one blank line after the description. Format as bullet points starting with '- ', using imperative, present tense, concise phrases (max 72 characters per line).
- **Footer**: Optional. Include 'BREAKING CHANGE: <description>' for breaking changes if the description is insufficient. Use '!' before ':' in the subject line for breaking changes (e.g., feat(api)!). Optionally include issue references (e.g., 'Closes #123').
- **Special Cases**:
  - For initial commits, use 'chore: init'.
  - For revert commits, use 'revert: <reverted commit subject line>' with a 'Refs: <commit SHA>' footer if applicable.
  - Merge commits are not generated by this script.
- **Examples**:
  - feat(ui): add dark mode toggle
    - Implement toggle for dark/light mode in UI
    - Update CSS for theme consistency
  - fix(api): resolve incorrect response status
    - Fix HTTP status code in error responses
    - Add tests for status validation
  - feat(auth)!: require oauth for all endpoints
    - Enforce OAuth token for API access
    - Update auth middleware
    BREAKING CHANGE: endpoints require OAuth token
  - docs: update contributing guidelines
  - refactor(core): simplify request handler
    - Remove obsolete timeout logic
    - Streamline error handling
  - perf: optimize database query with indexing
    - Add indexes to user table
    - Reduce query time by 30%
  - test(db): add tests for user query endpoints
    - Add unit tests for GET queries
    - Mock database for test isolation
  - build: update webpack to version 5.0.0
    - Upgrade build dependencies
    - Optimize bundle size
  - ops: configure automated backups
    - Set up daily backup cron job
    - Store backups in secure bucket
  - chore: init
  - revert: undo user authentication changes
    Refs: abc1234
- **Constraints**:
  - Use crisp, simple, professional English in present tense.
  - Ensure the message aligns with Semantic Versioning (feat/fix for MINOR, BREAKING CHANGE for MAJOR, others for PATCH).
  - Avoid vague terms (e.g., 'improve', 'change') unless specific.
  - If the diff is unclear or empty, return: 'Error: Unable to generate commit message from diff.'
- **Output**: Return only the commit message text, without explanations or markdown code fences.

Based on the diff, generate a commit message that meets these criteria."""


def get_staged_diff():
    """Get the diff of staged changes."""
    diff_content = run_command("git diff --cached")
    if not diff_content:
        print("No staged changes found. Please stage changes using 'git add' before running gcm.")
        sys.exit(1)
    return sanitize_text(diff_content)


def call_gemini_api(prompt):
    """Call the Google Gemini API with the given prompt."""
    api_key = os.getenv("GEMINI_API_KEY")
    if not api_key:
        print("Error: GEMINI_API_KEY environment variable is not set.")
        print("Please set it with your Google Gemini API key.")
        sys.exit(1)
    
    model = "gemini-2.5-flash"
    url = f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent"
    headers = {
        "x-goog-api-key": api_key,
        "Content-Type": "application/json"
    }
    data = {
        "contents": [{
            "role": "user",
            "parts": [{"text": prompt}]
        }]
    }
    
    try:
        response = requests.post(url, headers=headers, json=data, timeout=30)
        response.raise_for_status()
        return response.text
    except requests.RequestException as e:
        print(f"Error: Failed to fetch response from Gemini API: {e}")
        sys.exit(1)


def parse_gemini_response(response_text):
    """Parse the Gemini API response and extract the commit message."""
    response_text = sanitize_text(response_text)
    
    try:
        response_json = json.loads(response_text)
        message = response_json.get("candidates", [{}])[0].get("content", {}).get("parts", [{}])[0].get("text", "")
        message = re.sub(r'```', '', message).strip()
    except json.JSONDecodeError as e:
        print(f"Error: Failed to parse API response: {e}")
        print("The response may contain invalid characters or be malformed.")
        sys.exit(1)
    
    if message == "Error: Unable to generate commit message from diff.":
        print(message)
        sys.exit(1)
    
    return message


def generate_commit_message(commit_type, scope):
    """Generate a commit message using the Google Gemini API."""
    diff_content = get_staged_diff()
    prompt = PROMPT_TEMPLATE.format(
        diff_content=diff_content,
        commit_type=commit_type,
        scope=scope
    )
    
    response_text = call_gemini_api(prompt)
    return parse_gemini_response(response_text)