from dataclasses import dataclass, field
from enum import Enum
import glob
import os
import subprocess
import sys


class ArgType(Enum):
    ARG = 'arg'
    PATH = 'path'
    IGNORE = 'ignore'
    FILE = 'file'
    DIR = 'dir'


@dataclass
class ArgInfo:
    arg: str
    arg_type: ArgType
    path: str = ''
    files: set = field(default_factory=set)
    template_files: list = field(default_factory=list)
    template_dir: str = ''


class Command:
    '''
    This Command class holds all the information needed to construct
    a new command using templated files.
    '''

    def __init__(self, command: list):
        if not command:
            raise ValueError('command must be defined.')

        self.command: list[str] = command
        self.arg_info: list[ArgInfo] = self._getCommandInfo()

    def _getCommandInfo(self) -> list:
        '''
        This method takes the command the Command was initiated with and extracts
        any potential files from the command. This method will auto-run on object init.
        '''

        arg_info = []

        for arg in self.command:

            arg_to_path = glob.glob(os.path.expandvars(arg))

            if not arg_to_path:
                arg_info.append(
                    ArgInfo(
                        arg=arg,
                        arg_type=ArgType.ARG
                    )
                )

            for path in arg_to_path:
                path = os.path.realpath(os.path.expanduser(path))

                if 'pycache' in arg:
                    arg_info.append(
                        ArgInfo(
                            arg=arg,
                            arg_type=ArgType.IGNORE
                        )
                    )
                    continue

                if os.path.isfile(path):
                    arg_info.append(
                        ArgInfo(
                            arg=arg,
                            arg_type=ArgType.FILE,
                            path=path,
                            files={os.path.abspath(path)}
                        )
                    )

                elif os.path.isdir(path):
                    dir_files = set()

                    for root, dir, files in os.walk(path):
                        if '.git' in root:
                            continue

                        for file in files:
                            file = os.path.join(root, file)
                            ignore_paths = ['pycache', '.terraform']

                            if (os.path.isfile(file) and
                                    not any(x in file for x in ignore_paths)):
                                dir_files.add(file)

                    arg_info.append(
                        ArgInfo(
                            arg=arg,
                            arg_type=ArgType.DIR,
                            path=path,
                            files=dir_files
                        )
                    )
        return arg_info

    def generateCommand(self) -> list:
        '''
        This method generates the new_command attribute based on the
        arg_info generated by _extractFiles.s
        '''
        new_command = []

        if not self.arg_info:
            raise ValueError('Object does not have arg_info defined.')

        for arg_info in self.arg_info:
            if arg_info.arg_type == ArgType.DIR:
                new_command.append(arg_info.template_dir or arg_info.arg)
            elif arg_info.arg_type == ArgType.FILE:
                new_command.append(*arg_info.template_files or arg_info.arg)
            elif arg_info.arg_type == ArgType.IGNORE:
                continue
            elif arg_info.arg_type == ArgType.ARG:
                new_command.append(arg_info.arg)

        self.new_command = new_command

        return new_command

    def runCommand(self) -> subprocess.CompletedProcess:
        '''
        This method acts as a helper function to run the inputted command.
        '''
        try:
            result = subprocess.run(
                self.new_command,
                capture_output=True,
                text=True
            )
        except KeyboardInterrupt:
            print('--KeyboardInterrupt--', file=sys.stderr)
            exit(1)

        return result
