#!/usr/bin/env python3
"""
Changelog generation script for Text Texture Generator.
Generates changelog from git commits between tags.
"""

import os
import sys
import json
import subprocess
import argparse
from pathlib import Path
from typing import List, Dict, Optional, Tuple
from datetime import datetime
import re

def get_project_root() -> Path:
    """Get the project root directory."""
    return Path(__file__).parent.parent

def load_config() -> Dict:
    """Load build configuration."""
    config_path = get_project_root() / "build" / "config.json"
    with open(config_path, 'r') as f:
        return json.load(f)

def run_git_command(command: List[str]) -> Optional[str]:
    """Run a git command and return output."""
    try:
        result = subprocess.run(
            ['git'] + command,
            capture_output=True,
            text=True,
            check=True,
            cwd=get_project_root()
        )
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Git command failed: {' '.join(['git'] + command)}")
        print(f"Error: {e.stderr}")
        return None

def get_git_tags() -> List[str]:
    """Get all git tags sorted by version."""
    output = run_git_command(['tag', '-l', '--sort=-version:refname'])
    if output:
        return [tag for tag in output.split('\n') if tag.strip()]
    return []

def get_commits_between(from_ref: Optional[str], to_ref: str) -> List[Dict]:
    """Get commits between two references."""
    if from_ref:
        commit_range = f"{from_ref}..{to_ref}"
    else:
        commit_range = to_ref
    
    # Get commit info in a structured format
    format_str = "%H|%an|%ae|%ad|%s"
    output = run_git_command([
        'log', 
        '--pretty=format:' + format_str,
        '--date=iso',
        commit_range
    ])
    
    if not output:
        return []
    
    commits = []
    for line in output.split('\n'):
        if '|' in line:
            parts = line.split('|', 4)
            if len(parts) == 5:
                commits.append({
                    'hash': parts[0],
                    'author': parts[1],
                    'email': parts[2],
                    'date': parts[3],
                    'message': parts[4]
                })
    
    return commits

def categorize_commit(commit_message: str) -> Tuple[str, str]:
    """Categorize a commit based on its message."""
    message_lower = commit_message.lower()
    
    # Define categories and their patterns
    categories = {
        'Features': [
            r'^feat(\(.+\))?:', r'^add:', r'^implement:', 
            r'\bfeature\b', r'\badd\b.*\bfeature\b', r'\bnew\b.*\bfeature\b'
        ],
        'Bug Fixes': [
            r'^fix(\(.+\))?:', r'^bug:', r'^hotfix:', 
            r'\bfix\b', r'\bbug\b.*\bfix\b', r'\bresolve\b.*\bissue\b'
        ],
        'Improvements': [
            r'^improve(\(.+\))?:', r'^enhance(\(.+\))?:', r'^refactor(\(.+\))?:',
            r'\bimprove\b', r'\benhance\b', r'\boptimize\b', r'\brefactor\b'
        ],
        'Documentation': [
            r'^docs?(\(.+\))?:', r'^documentation:',
            r'\bdocs?\b', r'\bdocumentation\b', r'\breadme\b'
        ],
        'Build System': [
            r'^build(\(.+\))?:', r'^ci(\(.+\))?:', r'^chore(\(.+\))?:',
            r'\bbuild\b', r'\bci\b', r'\bchore\b', r'\bscript\b'
        ],
        'Tests': [
            r'^test(\(.+\))?:', r'\btest\b', r'\btesting\b'
        ]
    }
    
    # Check each category
    for category, patterns in categories.items():
        for pattern in patterns:
            if re.search(pattern, message_lower):
                # Clean up the message
                cleaned_message = commit_message
                # Remove conventional commit prefixes
                cleaned_message = re.sub(r'^(feat|fix|docs?|style|refactor|test|chore|build|ci|perf|improve|enhance|add|implement|bug|hotfix)(\(.+\))?:\s*', '', cleaned_message, flags=re.IGNORECASE)
                return category, cleaned_message
    
    # Default category
    return 'Other Changes', commit_message

def generate_changelog_content(version: str, commits: List[Dict], previous_version: Optional[str] = None) -> str:
    """Generate changelog content for a version."""
    lines = []
    
    # Header
    lines.append(f"## [{version}] - {datetime.now().strftime('%Y-%m-%d')}")
    lines.append("")
    
    if not commits:
        lines.append("No changes recorded.")
        lines.append("")
        return "\n".join(lines)
    
    # Categorize commits
    categorized = {}
    for commit in commits:
        category, message = categorize_commit(commit['message'])
        if category not in categorized:
            categorized[category] = []
        categorized[category].append({
            'message': message,
            'hash': commit['hash'][:8],
            'author': commit['author']
        })
    
    # Order categories by importance
    category_order = [
        'Features',
        'Bug Fixes', 
        'Improvements',
        'Documentation',
        'Build System',
        'Tests',
        'Other Changes'
    ]
    
    # Generate sections
    for category in category_order:
        if category in categorized:
            lines.append(f"### {category}")
            lines.append("")
            for item in categorized[category]:
                # Format: - Message (hash)
                lines.append(f"- {item['message']} ({item['hash']})")
            lines.append("")
    
    # Add comparison link if we have a previous version
    if previous_version:
        repo_url = "https://github.com/marcmintel/text-texture-generator"  # Adjust as needed
        lines.append(f"**Full Changelog**: {repo_url}/compare/{previous_version}...{version}")
        lines.append("")
    
    return "\n".join(lines)

def generate_full_changelog() -> str:
    """Generate complete changelog from all tags."""
    lines = []
    
    # Header
    lines.append("# Changelog")
    lines.append("")
    lines.append("All notable changes to this project will be documented in this file.")
    lines.append("")
    lines.append("The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),")
    lines.append("and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).")
    lines.append("")
    
    # Get tags
    tags = get_git_tags()
    
    if not tags:
        lines.append("## [Unreleased]")
        lines.append("")
        # Get all commits
        commits = get_commits_between(None, "HEAD")
        version_content = generate_changelog_content("Unreleased", commits)
        lines.append(version_content)
    else:
        # Process each version
        for i, tag in enumerate(tags):
            # Clean version (remove 'v' prefix if present)
            clean_version = tag.lstrip('v')
            
            # Get previous tag
            previous_tag = tags[i + 1] if i + 1 < len(tags) else None
            
            # Get commits for this version
            commits = get_commits_between(previous_tag, tag)
            
            # Generate content
            version_content = generate_changelog_content(clean_version, commits, previous_tag)
            lines.append(version_content)
    
    return "\n".join(lines)

def generate_changelog_for_version(version: str, output_file: Optional[Path] = None) -> str:
    """Generate changelog for a specific version."""
    tags = get_git_tags()
    
    # Find the version tag
    version_tag = None
    previous_tag = None
    
    for i, tag in enumerate(tags):
        if tag == version or tag == f"v{version}":
            version_tag = tag
            previous_tag = tags[i + 1] if i + 1 < len(tags) else None
            break
    
    if not version_tag:
        print(f"Warning: Version tag '{version}' not found. Using current HEAD.")
        version_tag = "HEAD"
        previous_tag = tags[0] if tags else None
    
    # Get commits
    commits = get_commits_between(previous_tag, version_tag)
    
    # Generate content
    content = generate_changelog_content(version, commits, previous_tag)
    
    # Write to file if specified
    if output_file:
        with open(output_file, 'w') as f:
            f.write(content)
        print(f"Changelog written to: {output_file}")
    
    return content

def main():
    """Main entry point."""
    parser = argparse.ArgumentParser(description="Generate changelog from git commits")
    parser.add_argument("--version", help="Generate changelog for specific version")
    parser.add_argument("--output", type=Path, help="Output file path")
    parser.add_argument("--full", action="store_true", help="Generate full changelog")
    
    args = parser.parse_args()
    
    try:
        if args.full:
            content = generate_full_changelog()
            output_file = args.output or get_project_root() / "CHANGELOG.md"
        elif args.version:
            content = generate_changelog_for_version(args.version, args.output)
            output_file = args.output
        else:
            # Default: generate for latest version
            tags = get_git_tags()
            if tags:
                latest_version = tags[0].lstrip('v')
                content = generate_changelog_for_version(latest_version, args.output)
                output_file = args.output
            else:
                print("No git tags found. Use --full to generate unreleased changelog.")
                sys.exit(1)
        
        # Write to file or print
        if output_file and not args.version:
            with open(output_file, 'w') as f:
                f.write(content)
            print(f"Changelog written to: {output_file}")
        elif not args.version:
            print(content)
        
    except Exception as e:
        print(f"❌ Changelog generation failed: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()