import os
import subprocess
import platform
import time

# registry keys can only be checked for Windows
if os.name == 'nt':
# winreg is a built in library for windows python users, no need to pip install
    import winreg

'''
This class is mostly aimed for Windows
I don't know how linux based operating systems act in a virtual machine
'''
class FileSystem:

    _KEYS = [
        r"SOFTWARE\Oracle\VirtualBox Guest Additions",
        r"HARDWARE\ACPI\DSDT\VBOX__",
        r"HARDWARE\ACPI\FADT\VBOX__",
        r"HARDWARE\ACPI\RSDT\VBOX__",
        r"SYSTEM\ControlSet001\Services\VBoxGuest",
        r"SYSTEM\ControlSet001\Services\VBoxMouse",
        r"SYSTEM\ControlSet001\Services\VBoxService",
        r"SYSTEM\ControlSet001\Services\VBoxSF",
        r"SYSTEM\ControlSet001\Services\VBoxVideo",
        r"SOFTWARE\VMware, Inc.\VMware Tools",
    ]

    _FILES = [
        r"C:\WINDOWS\system32\drivers\VBoxMouse.sys",
        r"C:\WINDOWS\system32\drivers\VBoxGuest.sys",
        r"C:\WINDOWS\system32\drivers\VBoxSF.sys",
        r"C:\WINDOWS\system32\drivers\VBoxVideo.sys",
        r"C:\WINDOWS\system32\vboxdisp.dll",
        r"C:\WINDOWS\system32\vboxhook.dll",
        r"C:\WINDOWS\system32\vboxmrxnp.dll",
        r"C:\WINDOWS\system32\vboxogl.dll",
        r"C:\WINDOWS\system32\vboxoglarrayspu.dll",
        r"C:\WINDOWS\system32\vboxoglcrutil.dll",
        r"C:\WINDOWS\system32\vboxoglerrorspu.dll",
        r"C:\WINDOWS\system32\vboxoglfeedbackspu.dll",
        r"C:\WINDOWS\system32\vboxoglpackspu.dll",
        r"C:\WINDOWS\system32\vboxoglpassthroughspu.dll",
        r"C:\WINDOWS\system32\vboxservice.exe",
        r"C:\WINDOWS\system32\vboxtray.exe",
        r"C:\WINDOWS\system32\VBoxControl.exe",
        r"C:\WINDOWS\system32\drivers\vmmouse.sys",
        r"C:\WINDOWS\system32\drivers\vmhgfs.sys",
        r"C:\WINDOWS\system32\drivers\vmusbmouse.sys",
        r"C:\WINDOWS\system32\drivers\vmkdb.sys",
        r"C:\WINDOWS\system32\drivers\vmrawdsk.sys",
        r"C:\WINDOWS\system32\drivers\vmmemctl.sys",
        r"C:\WINDOWS\system32\drivers\vm3dmp.sys",
        r"C:\WINDOWS\system32\drivers\vmci.sys",
        r"C:\WINDOWS\system32\drivers\vmsci.sys",
        r"C:\WINDOWS\system32\drivers\vmx_svga.sys"
    ]

    _PROCESSES = [
        "vboxservices.exe",
        "vboxservice.exe",
        "vboxtray.exe",
        "xenservice.exe",
        "VMSrvc.exe",
        "vemusrvc.exe",
        "VMUSrvc.exe",
        "qemu-ga.exe",
        "prl_cc.exe",
        "prl_tools.exe",
        "vmtoolsd.exe",
        "df5serv.exe",
    ]

    @staticmethod
    def check_vm_registry_keys():
        if os.name != 'nt':
            return 5, "VM REGISTRY KEYS are None.", "This test can only be run on Windows. Considering this test successful."

        score = 5
        description = f"REGISTRY KEYS will look for VM related keys."
        explanation = None

        try:
            reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
            
            for key in FileSystem._KEYS:
                try:
                    winreg.OpenKey(reg, key)
                    # Don't immediately fail on single registry key
                    score = 2
                    explanation = f"Found VM-related registry key: {key}"
                    # Continue checking other keys to build evidence
                except WindowsError as e:
                    # Error code 5 is access denied, which we should ignore
                    if e.winerror == 5:
                        continue
                    # Error code 2 means key doesn't exist, which is good
                    elif e.winerror == 2:
                        continue
                    else:
                        continue
                        
        except Exception as e:
            # If we can't access the registry at all, don't penalize
            score = 5
            explanation = "Could not access registry"

        return score, description, explanation

    @staticmethod
    def check_vm_files():
        if os.name != 'nt':
            return 5, "VM RELATED FILES are None.", "This test can only be run on Windows. Considering this test successful."

        score = 5
        description = f"FILES will look for VM related files."
        explanation = None

        for filepath in FileSystem._FILES:
            if os.path.exists(filepath):
                # Don't immediately fail on single file
                score = min(score, 2)  # Reduce score but don't set to 0
                if explanation:
                    explanation += f"\nFound VM-related file: {filepath}"
                else:
                    explanation = f"Found VM-related file: {filepath}"

        return score, description, explanation

    @staticmethod
    def check_vm_processes():
        if os.name != 'nt':
            return 5, "VM RELATED PROCESSES are None.", "This test can only be run on Windows. Considering this test successful."

        score = 5
        description = f"PROCESSES will look for VM related processes."
        explanation = None

        try:
            # Try multiple methods to get process list
            tasklist = ""
            try:
                # Try PowerShell first
                tasklist = subprocess.check_output('powershell "Get-Process | Select-Object ProcessName"', shell=True).decode()
            except:
                try:
                    # Try wmic as fallback
                    tasklist = subprocess.check_output('wmic process get name', shell=True).decode()
                except:
                    try:
                        # Try tasklist as last resort
                        tasklist = subprocess.check_output('tasklist', shell=True).decode()
                    except:
                        # If all methods fail, skip the check
                        score = 5
                        explanation = "Process check skipped - could not access process list"
                        return score, description, explanation

            # Check for VM processes in the list we got
            tasklist = tasklist.lower()
            for process in FileSystem._PROCESSES:
                if process.lower() in tasklist:
                    # Don't immediately fail on single process
                    score = min(score, 2)  # Reduce score but don't set to 0
                    if explanation:
                        explanation += f"\nFound VM-related process: {process}"
                    else:
                        explanation = f"Found VM-related process: {process}"

        except Exception as e:
            # Don't penalize for any errors
            score = 5
            explanation = "Process check skipped due to access restrictions."

        return score, description, explanation

    @staticmethod
    def check_wifi_connections():
        return 5, "WIFI check disabled.", None

    @staticmethod
    def check_application_files():
        return 5, "Application files check disabled.", None

    @staticmethod
    def check_prev_logins():
        if os.name != 'nt':
            return 5, "PREV LOGINS are None.", "This test can only be run on Windows. Considering this test successful."

        try:
            # Alternative way to check user activity without win32net
            # Get current username
            current_user = os.getenv('USERNAME')
            
            # Try to get user profile directory
            users_path = os.path.join(os.environ['SystemDrive'] + '\\', 'Users')
            if not os.path.exists(users_path):
                score = 5  # Don't penalize if we can't access the directory
                return score, "PREV LOGINS check skipped.", "Could not access Users directory."

            # Get all user directories excluding system ones
            users = []
            try:
                users = [d for d in os.listdir(users_path) 
                        if os.path.isdir(os.path.join(users_path, d)) 
                        and d.lower() not in ['public', 'default', 'default user', 'all users', 'defaultapppool', 'system']
                        and not d.endswith('$')]
            except PermissionError:
                score = 5  # Don't penalize if we don't have permission
                return score, "PREV LOGINS check skipped.", "Permission denied accessing Users directory."

            # Get the creation time of user profiles as a basic metric
            total_days = 0
            for user in users:
                user_path = os.path.join(users_path, user)
                if os.path.exists(user_path):
                    creation_time = os.path.getctime(user_path)
                    days_since_creation = (time.time() - creation_time) / (24 * 3600)
                    total_days += days_since_creation

            description = f"PREV LOGINS will look for user profiles and their age."
            explanation = f"Found {len(users)} user profiles with combined age of {int(total_days)} days."

            # Check for known sandbox users
            sandbox_users = ['wdagutilityaccount', 'vagrant', 'sandbox']
            if current_user and current_user.lower() in sandbox_users:
                score = 0
                explanation = f"Detected sandbox user account: {current_user}"
            elif current_user:
                score = 5
                explanation = f"User account: {current_user}"
                
                # Try to get profile age if possible
                try:
                    if users and current_user.lower() in [u.lower() for u in users]:
                        user_path = os.path.join(users_path, current_user)
                        creation_time = os.path.getctime(user_path)
                        days = (time.time() - creation_time) / (24 * 3600)
                        explanation += f" - Profile age: {int(days)} days"
                except:
                    pass  # Ignore errors when trying to get profile age
            else:
                score = 5  # Don't penalize if we can't detect username
                explanation = "Could not detect current user"

        except Exception as e:
            score = 5  # Give benefit of doubt on all errors
            explanation = f"Login check skipped due to access restrictions."
            
        return score, description, explanation
