#!/usr/bin/env python3
"""
Package validation script for Text Texture Generator addon.
Validates ZIP packages to ensure they meet Blender addon requirements.
"""

import os
import sys
import json
import zipfile
import tempfile
from pathlib import Path
from typing import Dict, List, Tuple, Optional
import argparse

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)

class ValidationResult:
    """Holds validation results."""
    def __init__(self):
        self.errors = []
        self.warnings = []
        self.info = []
        self.is_valid = True
    
    def add_error(self, message: str):
        """Add an error message."""
        self.errors.append(message)
        self.is_valid = False
    
    def add_warning(self, message: str):
        """Add a warning message."""
        self.warnings.append(message)
    
    def add_info(self, message: str):
        """Add an info message."""
        self.info.append(message)
    
    def print_results(self):
        """Print validation results."""
        if self.info:
            print("\n📋 Information:")
            for msg in self.info:
                print(f"  ℹ️  {msg}")
        
        if self.warnings:
            print("\n⚠️  Warnings:")
            for msg in self.warnings:
                print(f"  ⚠️  {msg}")
        
        if self.errors:
            print("\n❌ Errors:")
            for msg in self.errors:
                print(f"  ❌ {msg}")
        else:
            print("\n✅ No errors found!")
        
        print(f"\nValidation Result: {'✅ PASS' if self.is_valid else '❌ FAIL'}")

def validate_zip_structure(zip_path: Path, config: Dict) -> ValidationResult:
    """Validate the ZIP file structure."""
    result = ValidationResult()
    addon_id = config['project']['id']
    
    try:
        with zipfile.ZipFile(zip_path, 'r') as zip_file:
            file_list = zip_file.namelist()
            
            # Check if all files are within the addon directory
            addon_root = f"{addon_id}/"
            files_in_root = [f for f in file_list if f.startswith(addon_root)]
            
            if len(files_in_root) != len(file_list):
                result.add_error(f"Some files are not in the addon root directory '{addon_root}'")
            
            result.add_info(f"ZIP contains {len(file_list)} files")
            
            # Required files
            required_files = [
                f"{addon_id}/__init__.py",
                f"{addon_id}/blender_manifest.toml"
            ]
            
            for required_file in required_files:
                if required_file not in file_list:
                    result.add_error(f"Missing required file: {required_file}")
                else:
                    result.add_info(f"Found required file: {required_file}")
            
            # Check for excluded files
            exclude_patterns = config['build']['exclude_patterns']
            for file_path in file_list:
                for pattern in exclude_patterns:
                    if pattern in file_path:
                        result.add_warning(f"Excluded pattern found in package: {file_path}")
            
    except zipfile.BadZipFile:
        result.add_error("Invalid ZIP file")
    except Exception as e:
        result.add_error(f"Error reading ZIP file: {e}")
    
    return result

def validate_manifest(zip_path: Path, config: Dict, version_type: str) -> ValidationResult:
    """Validate the blender_manifest.toml file."""
    result = ValidationResult()
    addon_id = config['project']['id']
    manifest_path = f"{addon_id}/blender_manifest.toml"
    
    try:
        with zipfile.ZipFile(zip_path, 'r') as zip_file:
            if manifest_path not in zip_file.namelist():
                result.add_error("blender_manifest.toml not found in package")
                return result
            
            # Read and parse manifest
            manifest_content = zip_file.read(manifest_path).decode('utf-8')
            result.add_info("Successfully read blender_manifest.toml")
            
            # Check for required fields
            required_fields = ['id', 'version', 'name', 'blender_version_min']
            for field in required_fields:
                if f'{field} = ' not in manifest_content:
                    result.add_error(f"Missing required field in manifest: {field}")
            
            # Validate specific values
            if f'id = "{addon_id}"' not in manifest_content:
                result.add_error(f"Incorrect addon ID in manifest (expected: {addon_id})")
            
            # Check version format
            import re
            version_pattern = r'version = "(\d+\.\d+\.\d+)"'
            version_match = re.search(version_pattern, manifest_content)
            if version_match:
                version = version_match.group(1)
                result.add_info(f"Package version: {version}")
            else:
                result.add_error("Invalid or missing version format in manifest")
            
    except Exception as e:
        result.add_error(f"Error validating manifest: {e}")
    
    return result

def validate_constants(zip_path: Path, config: Dict, version_type: str) -> ValidationResult:
    """Validate the constants.py file for feature flags."""
    result = ValidationResult()
    addon_id = config['project']['id']
    constants_path = f"{addon_id}/utils/constants.py"
    
    try:
        with zipfile.ZipFile(zip_path, 'r') as zip_file:
            if constants_path not in zip_file.namelist():
                result.add_error("utils/constants.py not found in package")
                return result
            
            # Read constants file
            constants_content = zip_file.read(constants_path).decode('utf-8')
            result.add_info("Successfully read utils/constants.py")
            
            # Check for feature flags
            if 'ENABLED_FEATURES' not in constants_content:
                result.add_error("ENABLED_FEATURES not found in constants.py")
            
            if 'VERSION_TYPE' not in constants_content:
                result.add_error("VERSION_TYPE not found in constants.py")
            else:
                # Check if version type matches expected
                if f'VERSION_TYPE = "{version_type}"' not in constants_content:
                    result.add_warning(f"VERSION_TYPE might not match expected type: {version_type}")
            
            # Check for version limits functions
            if 'get_version_limits' not in constants_content:
                result.add_error("get_version_limits function not found in constants.py")
            
    except Exception as e:
        result.add_error(f"Error validating constants: {e}")
    
    return result

def validate_version_differences(zip_path: Path, config: Dict, version_type: str) -> ValidationResult:
    """Validate version-specific content and file exclusions."""
    result = ValidationResult()
    addon_id = config['project']['id']
    
    # Load exclusions configuration
    exclusions_path = get_project_root() / "build" / "file_exclusions.json"
    try:
        with open(exclusions_path, 'r') as f:
            exclusions_config = json.load(f)
    except Exception as e:
        result.add_error(f"Could not load file exclusions config: {e}")
        return result
    
    version_config = exclusions_config['version_configs'].get(version_type, {})
    excluded_files = version_config.get('exclude_files', [])
    excluded_patterns = version_config.get('exclude_patterns', [])
    
    try:
        with zipfile.ZipFile(zip_path, 'r') as zip_file:
            file_list = zip_file.namelist()
            
            # Check that excluded files are actually excluded in free version
            if version_type == "free":
                excluded_found = []
                for excluded_file in excluded_files:
                    full_path = f"{addon_id}/{excluded_file}"
                    if full_path in file_list or any(f.startswith(full_path) for f in file_list):
                        excluded_found.append(excluded_file)
                
                if excluded_found:
                    result.add_error(f"Free version contains excluded files: {excluded_found}")
                else:
                    result.add_info(f"Correctly excluded {len(excluded_files)} premium files from free version")
                
                # Check for excluded patterns
                pattern_violations = []
                for file_path in file_list:
                    for pattern in excluded_patterns:
                        if pattern.strip('*').lower() in file_path.lower():
                            pattern_violations.append((file_path, pattern))
                
                if pattern_violations:
                    result.add_warning(f"Files matching excluded patterns found: {pattern_violations}")
            
            elif version_type == "full":
                # Full version should contain premium files
                missing_premium = []
                for premium_file in excluded_files:
                    full_path = f"{addon_id}/{premium_file}"
                    if full_path not in file_list and not any(f.startswith(full_path) for f in file_list):
                        missing_premium.append(premium_file)
                
                if missing_premium:
                    result.add_error(f"Full version missing premium files: {missing_premium}")
                else:
                    result.add_info(f"Full version correctly includes all {len(excluded_files)} premium features")
            
            # Validate UI helper functions are present in constants.py
            constants_path = f"{addon_id}/utils/constants.py"
            if constants_path in file_list:
                constants_content = zip_file.read(constants_path).decode('utf-8')
                ui_functions = [
                    'get_version_badge_text',
                    'get_version_badge_icon',
                    'get_upgrade_hint_text',
                    'should_show_feature_lock',
                    'get_feature_availability_text',
                    'get_version_info_details'
                ]
                
                missing_ui_functions = []
                for func in ui_functions:
                    if func not in constants_content:
                        missing_ui_functions.append(func)
                
                if missing_ui_functions:
                    result.add_error(f"Missing UI helper functions in constants.py: {missing_ui_functions}")
                else:
                    result.add_info("All UI helper functions present in constants.py")
            
    except Exception as e:
        result.add_error(f"Error validating version differences: {e}")
    
    return result

def validate_feature_flags(zip_path: Path, config: Dict) -> ValidationResult:
    """Validate the feature flags system."""
    result = ValidationResult()
    addon_id = config['project']['id']
    feature_flags_path = f"{addon_id}/utils/feature_flags.py"
    
    try:
        with zipfile.ZipFile(zip_path, 'r') as zip_file:
            if feature_flags_path not in zip_file.namelist():
                result.add_warning("utils/feature_flags.py not found in package")
                return result
            
            # Read feature flags file
            feature_content = zip_file.read(feature_flags_path).decode('utf-8')
            result.add_info("Successfully read utils/feature_flags.py")
            
            # Check for essential classes and functions
            required_items = [
                'FeatureManager',
                'is_feature_enabled',
                'require_feature',
                'feature_required',
                'FeatureNotAvailableError'
            ]
            
            for item in required_items:
                if item not in feature_content:
                    result.add_warning(f"Missing feature flag component: {item}")
                else:
                    result.add_info(f"Found feature flag component: {item}")
            
    except Exception as e:
        result.add_error(f"Error validating feature flags: {e}")
    
    return result

def detect_version_type(zip_path: Path) -> Optional[str]:
    """Detect version type from ZIP filename or contents."""
    filename = zip_path.name.lower()
    if '_free' in filename:
        return 'free'
    elif 'free' in filename:
        return 'free'
    else:
        return 'full'  # Default assumption

def validate_package(zip_path: Path, version_type: Optional[str] = None) -> bool:
    """Validate a complete addon package."""
    if not zip_path.exists():
        print(f"❌ Package file not found: {zip_path}")
        return False
    
    if version_type is None:
        version_type = detect_version_type(zip_path)
    
    print(f"🔍 Validating package: {zip_path.name}")
    print(f"📋 Detected version type: {version_type}")
    print("=" * 60)
    
    config = load_config()
    overall_valid = True
    
    # Structure validation
    print("Validating ZIP structure...")
    structure_result = validate_zip_structure(zip_path, config)
    structure_result.print_results()
    overall_valid &= structure_result.is_valid
    
    print("\n" + "─" * 60)
    
    # Manifest validation
    print("Validating blender_manifest.toml...")
    manifest_result = validate_manifest(zip_path, config, version_type)
    manifest_result.print_results()
    overall_valid &= manifest_result.is_valid
    
    print("\n" + "─" * 60)
    
    # Constants validation
    print("Validating utils/constants.py...")
    constants_result = validate_constants(zip_path, config, version_type)
    constants_result.print_results()
    overall_valid &= constants_result.is_valid
    
    print("\n" + "─" * 60)
    
    # Feature flags validation
    print("Validating feature flags system...")
    features_result = validate_feature_flags(zip_path, config)
    features_result.print_results()
    overall_valid &= features_result.is_valid
    
    print("\n" + "─" * 60)
    
    # Version differences validation
    print("Validating version-specific content and exclusions...")
    version_diff_result = validate_version_differences(zip_path, config, version_type)
    version_diff_result.print_results()
    overall_valid &= version_diff_result.is_valid
    
    print("\n" + "=" * 60)
    print(f"Overall Validation: {'✅ PASS' if overall_valid else '❌ FAIL'}")
    
    return overall_valid

def main():
    """Main entry point."""
    parser = argparse.ArgumentParser(description="Validate Text Texture Generator addon package")
    parser.add_argument("package", type=Path, help="Path to ZIP package to validate")
    parser.add_argument("--type", choices=["free", "full"], help="Expected version type")
    
    args = parser.parse_args()
    
    try:
        is_valid = validate_package(args.package, args.type)
        sys.exit(0 if is_valid else 1)
    except Exception as e:
        print(f"❌ Validation failed: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()