import os
import subprocess

import psutil


class OSAProcesses:
    """Enhanced process management for SysPack"""

    def __init__(self, osaf_core):
        self.osaf_core = osaf_core
        self.log = osaf_core.log

    def exec(self, command, env=None, workdir=None, as_user=None, log=False,
             loglvl='info', readstring=False, readlist=False, timeout=300, encoding=None):
        """Enhanced execution with timeout and better security"""

        if encoding is None:
            if self.osaf_core.utils.is_windows():
                encoding = 'cp866'  # Стандартная кодировка Windows console
            else:
                encoding = 'utf-8'

        returncode = -42

        my_env = os.environ.copy()
        if env is not None:
            my_env.update(env)

        # Security: validate and sanitize user input
        if as_user:
            if not self._is_valid_username(as_user):
                self.log.e(f"Invalid username: {as_user}")
                return returncode, None

            has_sudo = self.osaf_core.command_probe(['sudo', '-h'])
            if not has_sudo:
                self.log.e("sudo command not found")
                return returncode, None
            command = ['sudo', '-u', as_user] + command

        readstring_output = None
        readlist_output = []
        r_data = None
        proc = None

        prev_dir = None
        if workdir:
            prev_dir = os.getcwd()
            os.chdir(workdir)

        try:
            if log or readstring or readlist:
                if readstring and not (readlist or log):
                    proc = subprocess.Popen(command, env=my_env, stdout=subprocess.PIPE,
                                            text=True, encoding=encoding)
                    readstring_output, err = proc.communicate(timeout=timeout)
                    returncode = proc.returncode
                    r_data = readstring_output

                elif readlist or log and not readstring:
                    proc = subprocess.Popen(command, env=my_env, stdout=subprocess.PIPE,
                                            text=True, encoding=encoding)
                    output_lines = []
                    try:
                        for line in iter(proc.stdout.readline, ''):
                            if not line:
                                break
                            line = line.rstrip()
                            if readlist:
                                output_lines.append(line)
                            if log:
                                if loglvl == 'info':
                                    self.log.i(f'[exec {command[0]}] {line}')
                                elif loglvl == 'debug':
                                    self.log.d(f'[exec {command[0]}] {line}')
                                elif loglvl == 'error':
                                    self.log.e(f'[exec {command[0]}] {line}')

                        proc.wait(timeout=timeout)
                        returncode = proc.returncode
                        r_data = output_lines
                    except subprocess.TimeoutExpired:
                        proc.kill()
                        raise
            else:
                result = subprocess.run(command, env=my_env, timeout=timeout)
                returncode = result.returncode

        except subprocess.TimeoutExpired:
            self.log.e(f"Command timed out: {command}")
            returncode = -1
        except Exception as e:
            self.log.e(f"Command execution error: {e}")
            returncode = -1

        finally:
            if workdir and prev_dir:
                os.chdir(prev_dir)

        self.log.d(f'Command {command} exited with code {returncode}')
        return returncode, r_data

    def _is_valid_username(self, username: str) -> bool:
        """Basic username validation"""
        import re
        return bool(re.match(r'^[a-z_][a-z0-9_-]*$', username))

    def kill(self, pid):
        if self.osaf_core.utils.is_windows():
            self.exec(['taskkill', '/PID', str(pid), '/F'])
        else:
            self.exec(['kill', str(pid)])

    def is_process_running(self, process_name: str) -> bool:
        """Check if process is running"""
        try:
            for proc in psutil.process_iter(['name']):
                if process_name.lower() in proc.info['name'].lower():
                    return True
            return False
        except psutil.Error:
            return False