# -*- coding: utf-8 -*-
""" hapi - CraftPilot
Author  : Cheneyshen
Email   : cheneyshen@tencent.com
"""

import logging
from .hdata import *
from .hgeo import HGeo, HGeoMesh, HGeoCurve, HGeoHeightfield, HGeoVolume, HGeoInstancer
from .hsession import HSession, HSessionManager, HSessionPool, HSessionTask
from .hnode import HNode, HInputNode, HHeightfieldInputNode, HHeightfieldInputVolumeNode
from .hasset import HAsset
from .hparm import *
from . import hapi as HAPI

# Version compatibility layer
from .compat import (
    get_houdini_version,
    get_version_info,
    has_feature,
    get_adapter,
    get_diagnostic_info,
    VersionInfo,
    UnsupportedVersionError,
    FeatureNotAvailableError
)

__craftpilot__version__ = "1.0"

import platform
import os
from ctypes import cdll
import re


# ========== 测试接口 ==========

def run_tests(verbose: bool = False, quick: bool = False, categories=None):
    """
    One-click test for pyhapi functionality completeness
    
    Args:
        verbose: Whether to output detailed information
        quick: Whether to run quick tests (< 30 seconds)
        categories: List of categories to test
                   Options: ['version', 'session', 'node', 'parm', 'geo', 'asset']
    
    Returns:
        TestResult object containing test result statistics
    
    Example:
        >>> import pyhapi
        >>> result = pyhapi.run_tests(quick=True)
        >>> print(f"Passed: {result.passed}/{result.total}")
        >>> print(f"Success Rate: {result.success_rate:.1f}%")
    """
    try:
        # Lazy import test module
        import sys
        from pathlib import Path
        
        # Add ci directory to path
        ci_dir = Path(__file__).parent.parent / 'ci'
        if str(ci_dir) not in sys.path:
            sys.path.insert(0, str(ci_dir))
        
        from pyhapi_tests import run_tests as _run_tests
        return _run_tests(verbose=verbose, quick=quick, categories=categories)
        
    except ImportError as e:
        print(f"[Error] Failed to import test module: {e}")
        print("Please ensure ci/pyhapi_tests.py exists")
        
        # Return a failed result
        from dataclasses import dataclass
        
        @dataclass
        class FailedTestResult:
            passed: int = 0
            failed: int = 1
            skipped: int = 0
            errors: int = 0
            duration: float = 0.0
            houdini_version: str = "Unknown"
            details: list = None
            
            @property
            def total(self):
                return 1
            
            @property
            def success_rate(self):
                return 0.0
        
        return FailedTestResult()


def __check_libpath(libpath):
    if not (os.path.exists(libpath) and os.path.isdir(libpath)):
        return False
    sys_platform = platform.system()
    libdll = None
    if sys_platform == "Windows":
        libdll = "libHAPIL.dll"
    elif sys_platform == "Linux":
        libdll = "libHAPIL.so"
    else:
        libdll = "libHAPIL.dylib"
    return os.path.exists(os.path.join(libpath, libdll))


def __ensure_hapi_path(libpath, houdini_path):
    SYS = platform.system()
    if SYS == "Windows":
        path_env = re.split(r'[;]', os.environ['PATH'])
        import sys
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS') or True: # only show craftpilot_server_setup.bat in exe mode.
            houdini_dso_path = os.path.normpath(os.getcwd())
            houdini_dso_path_not_exist = True
            if 'HOUDINI_DSO_PATH' in os.environ:
                houdini_dso_path_env = re.split(r'[;]', os.environ['HOUDINI_DSO_PATH'])
                houdini_dso_path_not_exist = houdini_dso_path not in (os.path.normpath(p) for p in houdini_dso_path_env)
            # kivlin. add hapi_path from reg if windows platform.
            # hou_reg_path = windows_get_hapi_path_from_reg()
            hou_reg_path = houdini_path
            if hou_reg_path != '':
                houdini_bin_path = os.path.normpath(os.path.join(hou_reg_path, "bin"))
                houdini_dsolib_path = os.path.normpath(os.path.join(hou_reg_path, "custom", "houdini", "dsolib"))
                houdin_bin_path_not_exist = houdini_bin_path not in (os.path.normpath(p) for p in path_env)
                houdini_dsolib_path_not_exist = houdini_dsolib_path not in (os.path.normpath(p) for p in path_env)
                
                # Auto-add environment variables temporarily (valid for current process)
                if houdin_bin_path_not_exist or houdini_dsolib_path_not_exist or houdini_dso_path_not_exist:
                    print("[HAPI Environment] Auto-configuring environment variables for current session...")
                    
                    # Add to PATH
                    if houdin_bin_path_not_exist:
                        path_env.append(houdini_bin_path)
                        print(f"  [+] Added to PATH: {houdini_bin_path}")
                    
                    if houdini_dsolib_path_not_exist:
                        path_env.append(houdini_dsolib_path)
                        print(f"  [+] Added to PATH: {houdini_dsolib_path}")
                    
                    # Update PATH environment variable
                    os.environ['PATH'] = ';'.join(path_env)
                    
                    # Add HOUDINI_DSO_PATH
                    if houdini_dso_path_not_exist:
                        if 'HOUDINI_DSO_PATH' in os.environ:
                            os.environ['HOUDINI_DSO_PATH'] += f";@/dso_^;@/dso;{houdini_dso_path}"
                        else:
                            os.environ['HOUDINI_DSO_PATH'] = f"@/dso_^;@/dso;{houdini_dso_path}"
                        print(f"  [+] Added to HOUDINI_DSO_PATH: {houdini_dso_path}")
                    
                    print("[HAPI Environment] Configuration complete (temporary, valid for this session only)")
                    print()
                    
                    # Optional: generate permanent setup bat file (for manual execution)
bat_filename = os.path.join(os.getcwd(), "craftpilot_server_setup.bat")
                    try:
                        with open(bat_filename, "w+", encoding="utf8") as f:
                            f.write('@echo off\n')
                            f.write('echo Setting up HAPI environment variables (requires Administrator privileges)...\n')
                            f.write('echo.\n')
                            if houdin_bin_path_not_exist or houdini_dsolib_path_not_exist:
                                f.write('setx "Path" "{};{};%Path%" /m\n'.format(houdini_bin_path, houdini_dsolib_path))
                            if houdini_dso_path_not_exist:
                                f.write('setx "HOUDINI_DSO_PATH" "%HOUDINI_DSO_PATH%;@/dso_^;@/dso;{}" /m\n'.format(houdini_dso_path))
                            f.write('echo.\n')
                            f.write('echo Environment variables set successfully!\n')
                            f.write('echo Please restart your computer for changes to take effect.\n')
                            f.write('pause\n')
                        print(f"[Optional] Permanent setup script generated: {bat_filename}")
                        print("           Run as Administrator if you want permanent environment variables.")
                        print()
                    except Exception as e:
                        pass  # Ignore bat file generation failure
                
                # Ensure path is in the list
                if houdini_bin_path not in path_env:
                    path_env.append(houdini_bin_path)
                if houdini_dsolib_path not in path_env:
                    path_env.append(houdini_dsolib_path)

    elif SYS == "Linux":
        path_env = re.split(r'[:]', os.environ['HDSO'])
    elif SYS == "Darwin":
        path_env = re.split(r'[;]', os.environ['PATH'])
    if libpath or __check_libpath(libpath):
        print(not libpath)
        if not os.path.normpath(libpath) in [os.path.normpath(p) for p in path_env]:
            path_env.append(libpath)
            os.environ['PATH'] = ';'.join(path_env)
        return True
    else:
        return any([__check_libpath(p) for p in path_env])

# ensure "import pyhapi" will not give error even cannot find libHAPIL
# call HSessionManager.get_or_create_default_session() to initialize, make it free of calling Initialize function
def load_library(windows_auto, houdini_path):
    __library_initialized__ = False
    if __ensure_hapi_path("", houdini_path):
        from . import hapi
        SYS = platform.system()
        if SYS == "Windows":
            bin_path = os.path.normpath(os.path.join(houdini_path, "bin"))

            if not windows_auto:
                # Import houdini_detector for version detection
                try:
                    # Try importing from parent directory
                    import sys
                    parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
                    if parent_dir not in sys.path:
                        sys.path.insert(0, parent_dir)
                    
                    from houdini_detector import detect_houdini_installations
                    
                    # Detect all Houdini versions
                    installations = detect_houdini_installations()
                    
                    if len(installations) > 1:
                        # Multiple versions, show selection list
                        print()
                        print("=" * 70)
                        print("Multiple Houdini versions detected, please select:")
                        print("=" * 70)
                        
                        for idx, info in enumerate(installations, 1):
                            marker = " (current)" if info['path'] == houdini_path else ""
                            print(f"  [{idx}] {info['display_name']}{marker}")
                            print(f"      Path: {info['path']}")
                            print(f"      Bin: {os.path.join(info['path'], 'bin')}")
                            print()
                        
                        print("-" * 70)
                        
                        # Get user selection
                        while True:
                            try:
                                default_idx = next((i for i, info in enumerate(installations, 1) 
                                                  if info['path'] == houdini_path), 1)
                                choice = input(f"Select version [1-{len(installations)}] (press Enter for current [{default_idx}]): ").strip()
                                
                                if not choice:
                                    # Use current version
                                    selected_idx = default_idx - 1
                                    break
                                
                                idx = int(choice) - 1
                                if 0 <= idx < len(installations):
                                    selected_idx = idx
                                    break
                                else:
                                    print(f"[Error] Please enter a number between 1 and {len(installations)}")
                            except ValueError:
                                print("[Error] Invalid input, please enter a number")
                            except KeyboardInterrupt:
                                print("\n[Cancelled] Using current version")
                                selected_idx = default_idx - 1
                                break
                        
                        selected = installations[selected_idx]
                        houdini_path = selected['path']
                        bin_path = os.path.normpath(os.path.join(houdini_path, "bin"))
                        
                        print()
                        print(f"[Selected] {selected['display_name']}")
                        print(f"           Bin path: {bin_path}")
                        print("=" * 70)
                        print()
                        
                    elif len(installations) == 1:
                        # Only one version, show in list format for consistency
                        print()
                        print("=" * 70)
                        print("Detected Houdini version:")
                        print("=" * 70)
                        
                        info = installations[0]
                        print(f"  [1] {info['display_name']}")
                        print(f"      Path: {info['path']}")
                        print(f"      Bin: {os.path.join(info['path'], 'bin')}")
                        print()
                        
                        print("-" * 70)
                        
                        # Get user confirmation
                        choice = input("Select version [1] (press Enter to confirm, 'c' for custom path): ").strip().lower()
                        
                        if choice == 'c':
                            # User wants custom path
                            custom_path = input("Enter custom Houdini installation path: ").strip()
                            if custom_path and os.path.exists(custom_path):
                                houdini_path = custom_path
                                bin_path = os.path.normpath(os.path.join(houdini_path, "bin"))
                                print(f"[Custom] Using custom path: {houdini_path}")
                            else:
                                print("[Warning] Invalid path, using detected version")
                                houdini_path = info['path']
                                bin_path = os.path.normpath(os.path.join(houdini_path, "bin"))
                        else:
                            # Use detected version (default)
                            houdini_path = info['path']
                            bin_path = os.path.normpath(os.path.join(houdini_path, "bin"))
                        
                        print()
                        print(f"[Selected] {info['display_name']}")
                        print(f"           Bin path: {bin_path}")
                        print("=" * 70)
                        print()
                    else:
                        # No version detected, use traditional method
                        input_info = "Please Input Houdini_Bin_Path: \n" + "(Default is " + bin_path + ")\n"
                        input_path = input(input_info)
                        if input_path != '':
                            bin_path = input_path
                            
                except ImportError as e:
                    # If houdini_detector cannot be imported, use traditional method
                    print(f"[Note] houdini_detector not available, using manual input")
                    input_info = "Please Input Houdini_Bin_Path: \n" + "(Default is " + bin_path + ")\n"
                    input_path = input(input_info)
                    if input_path != '':
                        bin_path = input_path

            hapi.HAPI_LIB = cdll.LoadLibrary(os.path.join(bin_path, "libHAPIL"))
        elif SYS == "Linux":
            hapi.HAPI_LIB = cdll.LoadLibrary("libHAPIL.so")
        elif SYS == "Darwin":
            hapi.HAPI_LIB = cdll.LoadLibrary("libHAPIL.dylib")
        __library_initialized__ = True
        print("HAPI Found")
    else:
        print("HAPI Not Found")
