# -*- coding: utf-8 -*-
"""
USD Converter Module Unit Tests
Author  : CraftPilot
"""

import os
import sys
import json
import unittest
import tempfile
import shutil

# Ensure pypapi module can be imported
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from pypapi import format_converter, usd_converter


class TestUSDConverter(unittest.TestCase):
    """USD Conversion Functionality Tests"""
    
    @classmethod
    def setUpClass(cls):
        """Test class initialization"""
        cls.test_data_dir = os.path.join(
            os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
            'temp',
            'usd_test_user',
            'usd_test_path'
        )
        
        # Create temporary test directory
        cls.temp_dir = tempfile.mkdtemp(prefix='usd_test_')
        print(f"\nTemporary test directory: {cls.temp_dir}")
    
    @classmethod
    def tearDownClass(cls):
        """Test class cleanup"""
        # Clean up temporary directory
        if os.path.exists(cls.temp_dir):
            shutil.rmtree(cls.temp_dir)
            print(f"Cleaned up temporary directory: {cls.temp_dir}")
    
    def test_usd_support_check(self):
        """Test USD support check"""
        print("\n=== Testing USD Support Check ===")
        
        usd_available = format_converter.check_usd_support()
        print(f"USD library availability: {usd_available}")
        
        if not usd_available:
            print("Warning: USD library not installed, some tests will be skipped")
            print("Install command: pip install usd-core")
        
        self.assertIsInstance(usd_available, bool)
    
    def test_usd_file_detection(self):
        """Test USD file format detection"""
        print("\n=== Testing USD File Format Detection ===")
        
        test_cases = [
            ('test.usd', True),
            ('test.usda', True),
            ('test.usdc', True),
            ('test.USD', True),
            ('test.USDA', True),
            ('test.obj', False),
            ('test.fbx', False),
            ('test.txt', False),
        ]
        
        for filename, expected in test_cases:
            result = format_converter.is_usd_file(filename)
            print(f"  {filename}: {result} (expected: {expected})")
            self.assertEqual(result, expected)
    
    @unittest.skipIf(not format_converter.check_usd_support(), "USD library not installed")
    def test_convert_simple_sphere(self):
        """Test converting simple sphere USD file"""
        print("\n=== Testing Simple Sphere Conversion ===")
        
        # Find test USD file
        test_usd_file = os.path.join(self.test_data_dir, 'sample_sphere.usda')
        
        if not os.path.exists(test_usd_file):
            self.skipTest(f"Test file does not exist: {test_usd_file}")
        
        print(f"Source file: {test_usd_file}")
        
        # Set output path
        output_json_file = os.path.join(self.temp_dir, 'sample_sphere.json')
        
        # Execute conversion
        json_data = format_converter.usd_to_json(test_usd_file, output_json_file)
        
        # Verify output file exists
        self.assertTrue(os.path.exists(output_json_file))
        print(f"Output file: {output_json_file}")
        
        # Verify JSON data structure
        self.assertIn('Output_Geo', json_data)
        self.assertIn('metadata', json_data)
        
        # Verify metadata
        metadata = json_data['metadata']
        print(f"Metadata: {json.dumps(metadata, indent=2)}")
        self.assertEqual(metadata['source_type'], 'usd')
        self.assertGreater(metadata['mesh_count'], 0)
        
        # Verify geometry data
        if 'attribs' in json_data['Output_Geo']:
            # Single mesh case
            attribs = json_data['Output_Geo']['attribs']
            self.assertIn('point', attribs)
            self.assertIn('P', attribs['point'])
            
            positions = attribs['point']['P']['data']
            print(f"Vertex count: {len(positions)}")
            self.assertGreater(len(positions), 0)
            
            if 'faces' in json_data['Output_Geo']:
                faces = json_data['Output_Geo']['faces']
                print(f"Face count: {len(faces)}")
                self.assertGreater(len(faces), 0)
        elif 'meshes' in json_data['Output_Geo']:
            # Multiple meshes case
            meshes = json_data['Output_Geo']['meshes']
            print(f"Mesh count: {len(meshes)}")
            self.assertGreater(len(meshes), 0)
    
    @unittest.skipIf(not format_converter.check_usd_support(), "USD library not installed")
    def test_batch_conversion(self):
        """Test batch conversion"""
        print("\n=== Testing Batch Conversion ===")
        
        if not os.path.exists(self.test_data_dir):
            self.skipTest(f"Test data directory does not exist: {self.test_data_dir}")
        
        # Find all USD files
        usd_files = [
            f for f in os.listdir(self.test_data_dir)
            if format_converter.is_usd_file(f)
        ]
        
        if not usd_files:
            self.skipTest("No USD files in test directory")
        
        print(f"Found {len(usd_files)} USD files:")
        for f in usd_files:
            print(f"  - {f}")
        
        # Execute batch conversion
        output_dir = os.path.join(self.temp_dir, 'batch_output')
        try:
            converted_files = format_converter.batch_convert_usd_to_json(
                self.test_data_dir,
                output_dir,
                recursive=False
            )
            
            print(f"\nSuccessfully converted {len(converted_files)} files:")
            for usd_path, json_path in converted_files:
                print(f"  {os.path.basename(usd_path)} -> {os.path.basename(json_path)}")
                self.assertTrue(os.path.exists(json_path))
        
        except Exception as e:
            print(f"Batch conversion error: {str(e)}")
            # Don't let single file failure cause entire test to fail
            pass
    
    @unittest.skipIf(not format_converter.check_usd_support(), "USD library not installed")
    def test_mesh_data_extraction(self):
        """Test mesh data extraction completeness"""
        print("\n=== Testing Mesh Data Extraction ===")
        
        test_usd_file = os.path.join(self.test_data_dir, 'sample_sphere.usda')
        
        if not os.path.exists(test_usd_file):
            self.skipTest(f"Test file does not exist: {test_usd_file}")
        
        # Convert USD
        json_data = format_converter.usd_to_json(test_usd_file)
        
        # Get first mesh data
        if 'attribs' in json_data['Output_Geo']:
            mesh_data = json_data['Output_Geo']
        elif 'meshes' in json_data['Output_Geo']:
            mesh_name = list(json_data['Output_Geo']['meshes'].keys())[0]
            mesh_data = json_data['Output_Geo']['meshes'][mesh_name]
        else:
            self.fail("Cannot find mesh data")
        
        # Verify data structure
        print("Verifying data structure:")
        
        # Verify vertex data
        self.assertIn('attribs', mesh_data)
        attribs = mesh_data['attribs']
        
        # Verify point attributes
        self.assertIn('point', attribs)
        self.assertIn('P', attribs['point'])
        print(f"  [OK] Vertex positions (P): {len(attribs['point']['P']['data'])} points")
        
        # Verify face data
        self.assertIn('faces', mesh_data)
        faces = mesh_data['faces']
        print(f"  [OK] Face data: {len(faces)} faces")
        
        # Verify each face is a triangle
        for i, face in enumerate(faces[:5]):  # Only check first 5
            self.assertEqual(len(face), 3, f"Face {i} is not a triangle: {face}")
        print(f"  [OK] All faces are triangulated")
        
        # Check optional attributes
        if 'N' in attribs.get('point', {}) or 'N' in attribs.get('vertex', {}):
            print(f"  [OK] Contains normal data")
        
        if 'uv' in attribs.get('point', {}) or 'uv' in attribs.get('vertex', {}):
            print(f"  [OK] Contains UV coordinates")
        
        if 'Cd' in attribs.get('point', {}):
            print(f"  [OK] Contains vertex colors")
    
    def test_error_handling(self):
        """Test error handling"""
        print("\n=== Testing Error Handling ===")
        
        # Test non-existent file
        with self.assertRaises(FileNotFoundError):
            format_converter.usd_to_json('nonexistent.usd')
        print("  [OK] Correctly handles file not found error")
        
        # Test non-USD file
        test_txt_file = os.path.join(self.temp_dir, 'test.txt')
        with open(test_txt_file, 'w') as f:
            f.write("not a usd file")
        
        with self.assertRaises(ValueError):
            format_converter.usd_to_json(test_txt_file)
        print("  [OK] Correctly handles invalid file format error")


class TestUSDIntegration(unittest.TestCase):
    """USD Conversion Integration Tests"""
    
    def test_format_converter_interface(self):
        """Test format_converter interface completeness"""
        print("\n=== Testing format_converter Interface ===")
        
        # Verify all USD-related functions are exported
        required_functions = [
            'is_usd_file',
            'check_usd_support',
            'usd_to_json',
            'usd_file_to_json_file',
            'batch_convert_usd_to_json',
        ]
        
        for func_name in required_functions:
            self.assertTrue(
                hasattr(format_converter, func_name),
                f"format_converter missing function: {func_name}"
            )
            print(f"  [OK] {func_name}")
    
    def test_usd_converter_module(self):
        """Test usd_converter module"""
        print("\n=== Testing usd_converter Module ===")
        
        # Verify module can be imported
        self.assertIsNotNone(usd_converter)
        print("  [OK] usd_converter module imported successfully")
        
        # Verify core functions exist
        core_functions = [
            'check_usd_support',
            'is_usd_file',
            'usd_to_pson_json',
            'extract_mesh_data',
        ]
        
        for func_name in core_functions:
            self.assertTrue(
                hasattr(usd_converter, func_name),
                f"usd_converter missing function: {func_name}"
            )
            print(f"  [OK] {func_name}")


def run_tests():
    """Run all tests"""
    print("=" * 70)
    print("USD Converter Module Tests")
    print("=" * 70)
    
    # Create test suite
    loader = unittest.TestLoader()
    suite = unittest.TestSuite()
    
    # Add tests
    suite.addTests(loader.loadTestsFromTestCase(TestUSDConverter))
    suite.addTests(loader.loadTestsFromTestCase(TestUSDIntegration))
    
    # Run tests
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    
    # Output summary
    print("\n" + "=" * 70)
    print("Test Summary")
    print("=" * 70)
    print(f"Total tests: {result.testsRun}")
    print(f"Passed: {result.testsRun - len(result.failures) - len(result.errors)}")
    print(f"Failed: {len(result.failures)}")
    print(f"Errors: {len(result.errors)}")
    print(f"Skipped: {len(result.skipped)}")
    
    return result.wasSuccessful()


if __name__ == '__main__':
    success = run_tests()
    sys.exit(0 if success else 1)
