from huey import SqliteHuey
import subprocess
import os
from .models import db, PrintFile, SystemConfig

huey = SqliteHuey(filename='huey_queue.db')

import configparser

@huey.task()
def slice_stl_task(file_id, stl_filepath, quality_preset=None, infill_density=None, support_enable=None, support_pattern=None, delete_stl=False):
    # This is run by the Huey worker
    # We need to create an app context to interact with the database
    from app import create_app
    app = create_app()
    with app.app_context():
        print_file = PrintFile.query.get(file_id)
        if not print_file:
            return

        # Cache variables and commit slicing status
        gcode_filename = print_file.filename.rsplit('.', 1)[0] + '.gcode'
        gcode_filepath = os.path.join(app.config['UPLOAD_FOLDER'], gcode_filename)
        print_file.status = 'slicing'
        db.session.commit()
        
        # Remove DB session to avoid locking the sqlite db during long slicing operations
        db.session.remove()

        try:
            # Create Cura engine options
            # use our local minimal configurations detached from the entire Cura framework
            print_config_path = os.path.abspath(os.path.join(app.root_path, '..', 'print_config'))
            printers_path = os.path.join(print_config_path, 'printers')
            extruders_path = os.path.join(print_config_path, 'extruders')
            materials_path = os.path.join(print_config_path, 'materials')
            presets_path = os.path.join(print_config_path, 'presets')
            env = os.environ.copy()
            env["CURA_ENGINE_SEARCH_PATH"] = f"{printers_path}:{extruders_path}:{materials_path}:{presets_path}"
            
            command = [
                "CuraEngine", "slice",
                "-j", os.path.join(printers_path, "creality_ender3v3se.def.json")
            ]
            
            # Apply quality presets if any
            if quality_preset:
                config = configparser.ConfigParser()
                preset_path = os.path.join(presets_path, 'creality', 'base', quality_preset)
                if os.path.exists(preset_path):
                    config.read(preset_path)
                    if config.has_section('values'):
                        for key, val in config.items('values'):
                            command.extend(['-s', f"{key}={val}"])
                            
            if infill_density is not None:
                command.extend(['-s', f"infill_sparse_density={infill_density}"])
                command.extend(['-s', f"infill_line_distance={100 / int(infill_density) if int(infill_density) > 0 else 9999}"])
                
            if support_enable is not None:
                command.extend(['-s', f"support_enable={'true' if support_enable == 'true' or support_enable == 'buildplate' else 'false'}"])
                command.extend(['-s', f"support_type={'buildplate' if support_enable == 'buildplate' else 'everywhere'}"])
                if support_pattern == 'tree':
                    command.extend(['-s', 'support_structure=tree'])
                    command.extend(['-s', 'support_tree_enable=true'])
                elif support_pattern and support_pattern != 'false':
                    command.extend(['-s', 'support_structure=normal'])
                    command.extend(['-s', f'support_pattern={support_pattern}'])
                            
            command.extend([
                "-l", stl_filepath,
                "-o", gcode_filepath
            ])
            
            app.logger.info(f"Running command: {' '.join(command)}")
            process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
            stdout, stderr = process.communicate()
            
            # Re-fetch print_file and update status
            print_file = PrintFile.query.get(file_id)
            if not print_file:
                return

            if process.returncode == 0:
                print_file.status = 'sliced'
            else:
                print_file.status = 'failed'
                app.logger.error(f"CuraEngine Error: {stderr.decode()}")
                
        except Exception as e:
            # Re-fetch in case of exception
            print_file = PrintFile.query.get(file_id)
            if print_file:
                print_file.status = 'failed'
            app.logger.error(f"Subprocess Exception: {e}")
            
        if delete_stl and os.path.exists(stl_filepath):
            try:
                os.remove(stl_filepath)
            except Exception as e:
                app.logger.error(f"Failed to delete temp STL {stl_filepath}: {e}")
                
        db.session.commit()
        db.session.remove()


@huey.task()
def merge_and_slice_task(file_id, inputs, merged_filepath, quality_preset=None, infill_density=None, support_enable=None, support_pattern=None, delete_stl=False):
    from app import create_app
    app = create_app()
    with app.app_context():
        from .models import PrintFile, db
        print_file = PrintFile.query.get(file_id)
        if not print_file:
            return

        db.session.remove()
        
        try:
            from stl_merger import merge_stls
            merge_stls(inputs, merged_filepath)
            
            # Now trigger the regular slicing task
            # We can just call the slicing logic or enqueue it
            slice_stl_task(file_id, merged_filepath, quality_preset, infill_density, support_enable, support_pattern, delete_stl=delete_stl)
        except Exception as e:
            print_file = PrintFile.query.get(file_id)
            if print_file:
                print_file.status = 'failed'
                db.session.commit()
            app.logger.error(f"Merge Exception: {e}")
        finally:
            db.session.remove()
