import os
import subprocess
import configparser
import uuid

class PrusaSlicerEngine:
    def __init__(self):
        self.name = "prusa_slicer"
        self.display_name = "PrusaSlicer"
        self.is_available = self._check_available()
        
    def _check_available(self):
        try:
            result = subprocess.run(["prusa-slicer", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            return b"Usage:" in result.stdout or b"Slic3r" in result.stdout or b"PrusaSlicer" in result.stdout or result.returncode == 0
        except (FileNotFoundError, OSError):
            return False

    def add_ini_keys(self, config_path, target_section, all_configs):
        config = configparser.ConfigParser(interpolation=None)
        config.read(config_path)
        if target_section not in config:
            config[target_section] = {}
        for k, v in config[target_section].items():
            all_configs[k] = v

    def slice(self, app, stl_filepath, gcode_filepath, **kwargs):
        """
        Slices via prusa-slicer CLI mapping standard kwargs to PRUSA parameters where possible.
        """
        try:
            # Base command
            command = [
                "/home/lhye200/AIO_3D_Print_Exp/prusaslicer/prusa-slicer",
                "-g", stl_filepath,
                "--output", gcode_filepath
            ]
            
            # Map quality, infill, supports to PrusaSlicer CLI arguments.
            # Example defaults, normally these would load from an .ini or be dynamically matched.
            quality_preset = kwargs.get('quality_preset')
            material_preset = kwargs.get('material_preset')
            infill_density = kwargs.get('infill_density')
            support_enable = kwargs.get('support_enable')
            support_pattern = kwargs.get('support_pattern')

            # print(support_pattern)
            all_configs = {}

            printer_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'printers', 'Ender3_V3_SE.ini')
            if os.path.exists(printer_ini):
                self.add_ini_keys(printer_ini, 'settings', all_configs)

            if quality_preset:
                q_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'quality', f"{quality_preset}.ini")
                if os.path.exists(q_ini):
                    self.add_ini_keys(q_ini, 'settings', all_configs)

            if material_preset:
                m_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'materials', f"{material_preset}.ini")
                if os.path.exists(m_ini):
                    self.add_ini_keys(m_ini, 'settings', all_configs)

            if infill_density is not None:
                command.extend([f"--fill-density={infill_density}%"])
                
            if support_enable and support_enable != 'false':
                # command.append("--support-material")
                if support_enable == 'buildplate':
                    command.append("--support-material-buildplate-only")
                # PrusaSlicer equivalent for tree supports => organic
                support_pattern_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'supports', f'{support_pattern}.ini')
                if os.path.exists(support_pattern_ini):
                    self.add_ini_keys(support_pattern_ini, 'settings', all_configs)
            else:
                # Load the default no_support.ini if no support is enabled
                no_support_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'supports', 'no_support.ini')
                if os.path.exists(no_support_ini):
                    self.add_ini_keys(no_support_ini, 'settings', all_configs)
                else:
                    all_configs['support_material'] = '0'
            
            tmp_ini_filename = f"tmp_{uuid.uuid4().hex}.ini"
            tmp_ini_path = os.path.join(app.config['UPLOAD_FOLDER'], tmp_ini_filename)
            print(f'****tmp_ini_path: {tmp_ini_path}')
            with open(tmp_ini_path, 'w') as f:
                for key, value in all_configs.items():
                    f.write(f"{key} = {value}\n")
            
            command.extend(["--load", tmp_ini_path])

            env = os.environ.copy()
            app.logger.info(f"Running command: {' '.join(command)}")
            
            process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
            stdout, stderr = process.communicate()
            
            if process.returncode == 0:
                # Clean up the temporary .ini file
                os.remove(tmp_ini_path)
                return True, None
            else:
                err_msg = stderr.decode() if stderr else "Unknown prusa-slicer error"
                app.logger.error(f"PrusaSlicer Error: {err_msg}")
                return False, err_msg
                
        except Exception as e:
            app.logger.error(f"PrusaSlicer Exception: {e}")
            return False, str(e)

    def get_quality_presets(self, app):
        all_files = [f for f in os.listdir(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer',"quality")) if f.endswith('.ini')]
        quality_presets = []
        for file in all_files:
            with open(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', "quality", file), 'r') as f:
                config = configparser.ConfigParser()
                config.read_file(f)
                if 'metadata' in config:
                    quality_presets.append({
                        'id': file.replace('.ini', ''),
                        'name': config['metadata'].get('show_name', file.replace('.ini', '').replace('_', ' '))
                    })
        return quality_presets

    def get_support_patterns(self, app):
        all_files = [f for f in os.listdir(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer',"supports")) if f.endswith('.ini')]
        support_presets = []
        for file in all_files:
            with open(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', "supports", file), 'r') as f:
                config = configparser.ConfigParser()
                config.read_file(f)
                if 'metadata' in config:
                    support_presets.append({
                        'id': file.replace('.ini', ''),
                        'name': config['metadata'].get('show_name', file.replace('.ini', '').replace('_', ' '))
                    })
        return support_presets

    def get_materials(self, app):
        try:
            path = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'materials')
            if not os.path.exists(path): return []
            files = [f for f in os.listdir(path) if f.endswith('.ini')]
            materials = []
            for f in files:
                materials.append({'id': f.replace('.ini', ''), 'name': f.replace('.ini', '').replace('_', ' ')})
            materials.sort(key=lambda x: x['name'])
            return materials
        except:
            return []
