import bpy
from bpy.types import Operator
from bpy.props import StringProperty, BoolProperty, EnumProperty
import os
import json
import time

# Import utility functions from main module
from ..presets.storage_system import (
    save_preset_unified, get_unified_preset_list,
    get_blend_file_presets, delete_blend_file_preset,
    refresh_presets_sync, ensure_presets_available,
    get_preset_path, get_persistent_preset_path
)
from ..utils import bl_info

class TEXT_TEXTURE_OT_save_preset(Operator):
    pass
    """Save current settings as preset"""
    bl_idname = "text_texture.save_preset"
    bl_label = "Save Preset"
    bl_description = "Save current settings as preset"
    bl_options = {'REGISTER', 'UNDO'}
    
    overwrite: bpy.props.BoolProperty(
        name="Overwrite",
        description="Overwrite existing preset",
        default=False
    )
    
    preset_name: bpy.props.StringProperty(
        name="Preset Name",
        description="Name of preset to save/overwrite",
        default=""
    )
    
    def execute(self, context):
        props = context.scene.text_texture_props
        preset_name = self.preset_name.strip() if self.preset_name else props.new_preset_name.strip()
        print(f"DEBUG: TEXT_TEXTURE_OT_save_preset.execute - Name: '{preset_name}', Overwrite: {self.overwrite}, SaveWithBlend: {props.save_with_blend_file}")
        
        if not preset_name:
            self.report({'ERROR'}, "Please enter a preset name")
            return {'CANCELLED'}
        
        # When overwriting from button, update the input field for consistency
        if self.preset_name and self.preset_name.strip():
            pass
            props.new_preset_name = preset_name
        
        # Validate preset name characters (avoid filesystem issues)
        import re
        # Allow Unicode letters, numbers, spaces, hyphens, underscores, and common accented characters
        # This pattern supports international characters like ö, ü, ñ, etc.
        if not re.match(r'^[\w\s\-]+$', preset_name, re.UNICODE):
            pass
            self.report({'ERROR'}, "Preset name contains invalid characters for filesystem compatibility")
            return {'CANCELLED'}
        
        # Additional check for filesystem-unsafe characters
        invalid_chars = ['<', '>', ':', '"', '|', '?', '*', '/', '\\']
        if any(char in preset_name for char in invalid_chars):
            pass
            self.report({'ERROR'}, f"Preset name cannot contain: {' '.join(invalid_chars)}")
            return {'CANCELLED'}
        
        # Check for duplicate names
        preset_file = os.path.join(get_persistent_preset_path(), f"{preset_name}.json")
        if os.path.exists(preset_file) and not self.overwrite:
            # Suggest alternative names
            base_name = preset_name
            counter = 1
            suggested_name = f"{base_name}_{counter}"
            while os.path.exists(os.path.join(get_persistent_preset_path(), f"{suggested_name}.json")):
                counter += 1
                suggested_name = f"{base_name}_{counter}"
            
            self.report({'ERROR'}, f"Preset '{preset_name}' already exists. Suggestion: try '{suggested_name}' or delete the existing preset first.")
            return {'CANCELLED'}
        
        # DETAILED LOGGING: Current shader properties before saving
        
        preset_data = {
            'text': props.text,
            'texture_width': props.texture_width,
            'texture_height': props.texture_height,
            'font_size': props.font_size,
            'padding': props.padding,
            'text_color': list(props.text_color),
            'background_transparent': props.background_transparent,
            'background_color': list(props.background_color),
            'font_path': props.font_path,
            'custom_font_path': props.custom_font_path,
            'use_custom_font': props.use_custom_font,
            'text_align': props.text_align,
            'prepend_text': props.prepend_text,
            'append_text': props.append_text,
            'prepend_margin': props.prepend_margin,
            'append_margin': props.append_margin,
            'prepend_append_layout': props.prepend_append_layout,
            'shader_type': props.shader_type,
            'shader_connection': props.shader_connection,
            'use_alpha': props.use_alpha,
            'emission_strength': props.emission_strength,
            'auto_assign_material': props.auto_assign_material,
            'custom_material_name': getattr(props, 'custom_material_name', ''),
            'disable_shadows': props.disable_shadows,
            'disable_reflections': props.disable_reflections,
            'disable_backfacing': props.disable_backfacing,
            'generate_normal_map': props.generate_normal_map,
            'normal_map_strength': props.normal_map_strength,
            'normal_map_blur_radius': props.normal_map_blur_radius,
            'normal_map_invert': props.normal_map_invert,
            'enable_stroke': props.enable_stroke,
            'stroke_width': props.stroke_width,
            'stroke_position': props.stroke_position,
            'stroke_color': list(props.stroke_color),
            'enable_shadow': props.enable_shadow,
            'shadow_offset_x': props.shadow_offset_x,
            'shadow_offset_y': props.shadow_offset_y,
            'shadow_blur': props.shadow_blur,
            'shadow_color': list(props.shadow_color),
            'shadow_spread': getattr(props, 'shadow_spread', 1.0),
            'enable_glow': props.enable_glow,
            'glow_blur': props.glow_blur,
            'glow_color': list(props.glow_color),
            'enable_blur': props.enable_blur,
            'blur_radius': props.blur_radius,
            # Text fitting properties
            'enable_text_fitting': props.enable_text_fitting,
            'text_fitting_mode': props.text_fitting_mode,
            'text_fitting_margin': props.text_fitting_margin,
            'min_font_size': props.min_font_size,
            'max_font_size': props.max_font_size,
            # Enhanced positioning properties
            'anchor_point': props.anchor_point,
            'position_mode': props.position_mode,
            'margin_x': props.margin_x,
            'margin_y': props.margin_y,
            'margins_linked': props.margins_linked,
            'offset_x': props.offset_x,
            'offset_y': props.offset_y,
            'manual_position_x': props.manual_position_x,
            'manual_position_y': props.manual_position_y,
            'manual_position_unit': props.manual_position_unit,
            'constrain_to_canvas': props.constrain_to_canvas,
            'image_overlays': []
        }
        
        # DETAILED LOGGING: Verify shader properties in preset_data
        
        # Save overlay data
        print(f"DEBUG: TEXT_TEXTURE_OT_save_preset - Found {len(props.image_overlays)} overlays in Scene")
        for i, overlay in enumerate(props.image_overlays):
            overlay_data = {
                'name': overlay.name,
                'component_type': overlay.component_type,
                'placement_mode': overlay.placement_mode,
                'text_content': overlay.text_content,
                'text_spacing': overlay.text_spacing,
                'image_spacing': overlay.image_spacing,
                'image_path': overlay.image_path,
                'x_position': overlay.x_position,
                'y_position': overlay.y_position,
                'scale': overlay.scale,
                'rotation': overlay.rotation,
                'z_index': overlay.z_index,
                'enabled': overlay.enabled
            }
            print(f"DEBUG: Extracted overlay {i}: '{overlay.name}' (Type: {overlay.component_type}, Mode: {overlay.placement_mode})")
            preset_data['image_overlays'].append(overlay_data)
        
        try:
            pass
            # Save based on checkbox setting - single destination only
            success = save_preset_unified(preset_data, preset_name, props.save_with_blend_file)
            
            if not success:
                pass
                self.report({'ERROR'}, f"Failed to save preset '{preset_name}'")
                return {'CANCELLED'}
            
            # Check if preset already exists in the collection and update it
            existing_preset = None
            for preset in props.presets:
                pass
                if preset.name == preset_name:
                    pass
                    existing_preset = preset
                    break
            
            target_index = None
            
            if not existing_preset:
                pass
                preset = props.presets.add()
                preset.name = preset_name
                preset.source = 'BLEND_FILE' if props.save_with_blend_file else 'PERSISTENT_FILE'
                target_index = len(props.presets) - 1
            else:
                pass
                # Update source based on where it was saved
                existing_preset.source = 'BLEND_FILE' if props.save_with_blend_file else 'PERSISTENT_FILE'
                for idx, collection_preset in enumerate(props.presets):
                    pass
                    if collection_preset.name == preset_name:
                        pass
                        target_index = idx
                        break
            
            # Provide clear feedback
            location = "with .blend file" if props.save_with_blend_file else "to persistent storage"
            
            if self.overwrite:
                pass
                self.report({'INFO'}, f"✅ Preset '{preset_name}' updated {location}")
            else:
                pass
                self.report({'INFO'}, f"✅ Preset '{preset_name}' saved {location}")
            
            if target_index is not None and hasattr(props, "active_preset_index"):
                pass
                props.active_preset_index = target_index
            if hasattr(props, "last_loaded_preset"):
                pass
                props.last_loaded_preset = preset_name
            
        except (OSError, IOError, json.JSONEncodeError) as e:
            pass
            self.report({'ERROR'}, f"Failed to save preset: {str(e)}")
            return {'CANCELLED'}
        except Exception as e:
            pass
            self.report({'ERROR'}, f"Unexpected error saving preset: {str(e)}")
            return {'CANCELLED'}
        
        return {'FINISHED'}
    
    def invoke(self, context, event):
        pass
        # Check if preset exists and prompt for overwrite
        props = context.scene.text_texture_props
        preset_name = props.new_preset_name.strip()
        
        if preset_name and preset_name != "New Preset":
            preset_file = os.path.join(get_persistent_preset_path(), f"{preset_name}.json")
            if os.path.exists(preset_file):
                return context.window_manager.invoke_confirm(self, event)
        
        return self.execute(context)
    
    def draw(self, context):
        pass
        layout = self.layout
        props = context.scene.text_texture_props
        preset_name = props.new_preset_name.strip()
        layout.label(text=f"Overwrite existing preset '{preset_name}'?")

class TEXT_TEXTURE_OT_save_preset_enter(Operator):
    pass
    """Save preset when Enter key is pressed in name field"""
    bl_idname = "text_texture.save_preset_enter"
    bl_label = "Save Preset (Enter)"
    bl_description = "Save current settings as preset when Enter is pressed"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        pass
        # Delegate to the main save preset operator
        return bpy.ops.text_texture.save_preset('INVOKE_DEFAULT')

class TEXT_TEXTURE_OT_clear_preset_search(Operator):
    pass
    """Clear the preset search filter"""
    bl_idname = "text_texture.clear_preset_search"
    bl_label = "Clear Preset Search"
    bl_description = "Reset the preset search field"
    bl_options = {'INTERNAL'}
    
    def execute(self, context):
        pass
        props = context.scene.text_texture_props
        if hasattr(props, "preset_search_query"):
            pass
            props.preset_search_query = ""
        return {'FINISHED'}

class TEXT_TEXTURE_OT_load_preset(Operator):
    pass
    """Load preset"""
    bl_idname = "text_texture.load_preset"
    bl_label = "Load Preset"
    bl_description = "Load the selected preset and optionally recreate materials. Non-undoable to preserve volatile properties."
    bl_options = {'REGISTER'}
    
    preset_name: StringProperty()
    
    def execute(self, context):
        pass
        props = context.scene.text_texture_props
        
        # DEFENSIVE PROGRAMMING: Ensure presets are available before loading
        if not ensure_presets_available():
            pass
            self.report({'ERROR'}, "Failed to ensure presets are loaded - cannot load preset")
            return {'CANCELLED'}
        
        # DETAILED LOGGING: Current shader properties before loading
        
        try:
            pass
            # Get unified preset list and find the one to load
            all_presets = get_unified_preset_list()
            
            if self.preset_name not in all_presets:
                pass
                self.report({'ERROR'}, f"Preset '{self.preset_name}' not found")
                return {'CANCELLED'}
            
            preset_data, source = all_presets[self.preset_name]
            
            # Update the UI checkbox to match the preset's source location
            props.save_with_blend_file = (source == 'BLEND_FILE')
            
            props.is_updating = True
            
            def _safe_assign_color(prop_array, new_values):
                for i, v in enumerate(new_values):
                    if i < len(prop_array):
                        prop_array[i] = v
            
            # Load text (previously skipped, this might be why users think it's broken)
            props.text = preset_data.get('text', props.text)
            props.texture_width = preset_data.get('texture_width', props.texture_width)
            props.texture_height = preset_data.get('texture_height', props.texture_height)
            props.font_size = preset_data.get('font_size', props.font_size)
            props.padding = preset_data.get('padding', props.padding)
            if 'text_color' in preset_data:
                _safe_assign_color(props.text_color, preset_data['text_color'])
            props.background_transparent = preset_data.get('background_transparent', props.background_transparent)
            if 'background_color' in preset_data:
                _safe_assign_color(props.background_color, preset_data['background_color'])
            props.font_path = preset_data.get('font_path', props.font_path)
            props.custom_font_path = preset_data.get('custom_font_path', props.custom_font_path)
            props.use_custom_font = preset_data.get('use_custom_font', props.use_custom_font)
            props.text_align = preset_data.get('text_align', props.text_align)
            
            # --- MIGRATION: LEGACY PREPEND/APPEND TEXT ---
            # Instead of loading into hidden legacy fields, process them immediately 
            # and append to the 'image_overlays' if valid payload exists.
            # Then ALWAYS clear the legacy properties so generation_engine ignores them.
            legacy_prependRaw = preset_data.get('prepend_text', '')
            legacy_appendRaw = preset_data.get('append_text', '')
            legacy_prepend = legacy_prependRaw.strip() if legacy_prependRaw else ''
            legacy_append = legacy_appendRaw.strip() if legacy_appendRaw else ''
            
            # Save for later processing when we handle image_overlays
            _has_legacy_migration = bool(legacy_prepend or legacy_append)
            _migrated_legacy_prepend = legacy_prepend
            _migrated_legacy_append = legacy_append
            
            # Force them clear on the property group
            props.prepend_text = ""
            props.append_text = ""
            props.prepend_margin = preset_data.get('prepend_margin', 0)
            props.append_margin = preset_data.get('append_margin', 0)
            props.prepend_append_layout = preset_data.get('prepend_append_layout', props.prepend_append_layout)
            
            # DETAILED LOGGING: Shader property loading with before/after values
            
            old_shader_type = props.shader_type
            props.shader_type = preset_data.get('shader_type', props.shader_type)
            
            old_shader_connection = props.shader_connection
            props.shader_connection = preset_data.get('shader_connection', props.shader_connection)
            
            old_use_alpha = props.use_alpha
            props.use_alpha = preset_data.get('use_alpha', props.use_alpha)
            
            old_emission_strength = props.emission_strength
            props.emission_strength = preset_data.get('emission_strength', props.emission_strength)
            
            old_auto_assign = props.auto_assign_material
            props.auto_assign_material = preset_data.get('auto_assign_material', props.auto_assign_material)
            
            if hasattr(props, 'custom_material_name'):
                props.custom_material_name = preset_data.get('custom_material_name', getattr(props, 'custom_material_name', ''))
            
            # CRITICAL SHADER PROPERTIES - Most detailed logging
            
            old_disable_shadows = props.disable_shadows
            new_disable_shadows = preset_data.get('disable_shadows', props.disable_shadows)
            props.disable_shadows = new_disable_shadows
            
            old_disable_reflections = props.disable_reflections
            new_disable_reflections = preset_data.get('disable_reflections', props.disable_reflections)
            props.disable_reflections = new_disable_reflections
            
            old_disable_backfacing = props.disable_backfacing
            new_disable_backfacing = preset_data.get('disable_backfacing', props.disable_backfacing)
            props.disable_backfacing = new_disable_backfacing
            
            
            props.generate_normal_map = preset_data.get('generate_normal_map', props.generate_normal_map)
            props.normal_map_strength = preset_data.get('normal_map_strength', props.normal_map_strength)
            props.normal_map_blur_radius = preset_data.get('normal_map_blur_radius', props.normal_map_blur_radius)
            props.normal_map_invert = preset_data.get('normal_map_invert', props.normal_map_invert)
            
            # Load stroke settings
            props.enable_stroke = preset_data.get('enable_stroke', props.enable_stroke)
            props.stroke_width = preset_data.get('stroke_width', props.stroke_width)
            props.stroke_position = preset_data.get('stroke_position', props.stroke_position)
            if 'stroke_color' in preset_data:
                _safe_assign_color(props.stroke_color, preset_data['stroke_color'])
            
            # Load shadow/glow settings
            props.enable_shadow = preset_data.get('enable_shadow', props.enable_shadow)
            props.shadow_offset_x = preset_data.get('shadow_offset_x', props.shadow_offset_x)
            props.shadow_offset_y = preset_data.get('shadow_offset_y', props.shadow_offset_y)
            props.shadow_blur = preset_data.get('shadow_blur', props.shadow_blur)
            if 'shadow_color' in preset_data:
                _safe_assign_color(props.shadow_color, preset_data['shadow_color'])
            # Handle new shadow_spread property with backward compatibility
            if hasattr(props, 'shadow_spread'):
                pass
                props.shadow_spread = preset_data.get('shadow_spread', 1.0)
            props.enable_glow = preset_data.get('enable_glow', props.enable_glow)
            props.glow_blur = preset_data.get('glow_blur', props.glow_blur)
            if 'glow_color' in preset_data:
                _safe_assign_color(props.glow_color, preset_data['glow_color'])
            
            # Load blur settings
            props.enable_blur = preset_data.get('enable_blur', props.enable_blur)
            props.blur_radius = preset_data.get('blur_radius', props.blur_radius)
            
            # Load text fitting settings
            props.enable_text_fitting = preset_data.get('enable_text_fitting', props.enable_text_fitting)
            props.text_fitting_mode = preset_data.get('text_fitting_mode', props.text_fitting_mode)
            props.text_fitting_margin = preset_data.get('text_fitting_margin', props.text_fitting_margin)
            props.min_font_size = preset_data.get('min_font_size', props.min_font_size)
            props.max_font_size = preset_data.get('max_font_size', props.max_font_size)
            
            # Load enhanced positioning settings
            props.anchor_point = preset_data.get('anchor_point', props.anchor_point)
            props.position_mode = preset_data.get('position_mode', props.position_mode)
            props.margin_x = preset_data.get('margin_x', props.margin_x)
            props.margin_y = preset_data.get('margin_y', props.margin_y)
            props.margins_linked = preset_data.get('margins_linked', props.margins_linked)
            props.offset_x = preset_data.get('offset_x', props.offset_x)
            props.offset_y = preset_data.get('offset_y', props.offset_y)
            props.manual_position_x = preset_data.get('manual_position_x', props.manual_position_x)
            props.manual_position_y = preset_data.get('manual_position_y', props.manual_position_y)
            props.manual_position_unit = preset_data.get('manual_position_unit', props.manual_position_unit)
            props.constrain_to_canvas = preset_data.get('constrain_to_canvas', props.constrain_to_canvas)
            
            # Deep data log for loading
            import json
            try:
                data_dump = json.dumps(preset_data, indent=2)
                print(f"DEBUG: TEXT_TEXTURE_OT_load_preset - LOADING PAYLOAD:\n{data_dump}")
            except Exception as e:
                print(f"DEBUG: Error dumping preset_data during load: {e}")
            
            # Load overlay data - ALWAYS check for data (bulletproof structure)
            if 'image_overlays' in preset_data:
                props.image_overlays.clear()
                overlay_data_list = preset_data.get('image_overlays', [])
                print(f"DEBUG: TEXT_TEXTURE_OT_load_preset - Found {len(overlay_data_list)} overlays in payload - REPLACING")
                for i, overlay_data in enumerate(overlay_data_list):
                    overlay = props.image_overlays.add()
                    overlay.name = overlay_data.get('name', 'Overlay')
                    overlay.component_type = overlay_data.get('component_type', overlay.component_type)
                    overlay.placement_mode = overlay_data.get('placement_mode', 'ABSOLUTE')
                    overlay.text_content = overlay_data.get('text_content', '')
                    overlay.text_spacing = overlay_data.get('text_spacing', overlay.text_spacing)
                    overlay.image_spacing = overlay_data.get('image_spacing', overlay.image_spacing)
                    overlay.image_path = overlay_data.get('image_path', '')
                    overlay.x_position = overlay_data.get('x_position', 0.0)
                    overlay.y_position = overlay_data.get('y_position', 0.0)
                    overlay.scale = overlay_data.get('scale', 1.0)
                    overlay.rotation = overlay_data.get('rotation', 0.0)
                    overlay.z_index = overlay_data.get('z_index', 0)
                    overlay.enabled = overlay_data.get('enabled', True)
                    print(f"DEBUG: Added overlay {i} to Scene: '{overlay.name}' (Type: {overlay.component_type}, Mode: {overlay.placement_mode})")
            else:
                print("DEBUG: TEXT_TEXTURE_OT_load_preset - 'image_overlays' key missing in payload - RETAINING current session data")
                # Wait, if image_overlays is missing we retain session data, but we might 
                # be migrating legacy fields from this preset. Let's make sure we actually 
                # apply the migration even if no 'image_overlays' key exists.
            
            # --- APPLY MIGRATED PREPEND/APPEND OVERLAYS ---
            # Ensure legacy text gets added to the current image_overlays list 
            # (either the fully replaced one or the retained one).
            if _has_legacy_migration:
                if _migrated_legacy_prepend:
                    overlay = props.image_overlays.add()
                    overlay.name = "Migrated Prefix"
                    overlay.component_type = 'TEXT'
                    overlay.placement_mode = 'PREPEND'
                    overlay.text_content = _migrated_legacy_prepend
                    # Handle margin which could be None in the preset, and map from old 0-100 logic
                    margin_val = preset_data.get('prepend_margin', 0)
                    overlay.text_spacing = min(30.0, float(margin_val) / 5.0) if margin_val else 0.0
                    overlay.enabled = True
                    print(f"DEBUG: Migrated legacy prepend_text to PREPEND layer")
                
                if _migrated_legacy_append:
                    overlay = props.image_overlays.add()
                    overlay.name = "Migrated Suffix"
                    overlay.component_type = 'TEXT'
                    overlay.placement_mode = 'APPEND'
                    overlay.text_content = _migrated_legacy_append
                    # Handle margin which could be None in the preset, and map from old 0-100 logic
                    margin_val = preset_data.get('append_margin', 0)
                    overlay.text_spacing = min(30.0, float(margin_val) / 5.0) if margin_val else 0.0
                    overlay.enabled = True
                    print(f"DEBUG: Migrated legacy append_text to APPEND layer")
            
            props.is_updating = False
            
            # FINAL VERIFICATION: Check what the properties actually are after loading
            
            # Always generate preview after loading preset
            # Import preview generation function
            from ..core.generation_engine import generate_preview
            generate_preview(props, props.preview_size if hasattr(props, 'preview_size') else 512, props.preview_size if hasattr(props, 'preview_size') else 512)
            
            # Also regenerate the main texture to update the 3D view immediately
            try:
                bpy.ops.text_texture.generate('EXEC_DEFAULT')
            except Exception as e:
                print(f"Failed to auto-generate texture: {e}")
            
            if hasattr(props, "last_loaded_preset"):
                pass
                props.last_loaded_preset = self.preset_name
            if hasattr(props, "active_preset_index"):
                pass
                for idx, preset in enumerate(props.presets):
                    pass
                    if preset.name == self.preset_name:
                        pass
                        props.active_preset_index = idx
                        break
            
            source_info = {
                'BLEND_FILE': "from blend file",
                'PERSISTENT_FILE': "from persistent storage",
                'LOCAL_FILE': "from legacy storage"
            }.get(source, "from unknown source")
            self.report({'INFO'}, f"✅ Loaded preset '{self.preset_name}' {source_info}")
            
        except (OSError, IOError) as e:
            pass
            props.is_updating = False
            self.report({'ERROR'}, f"Failed to read preset file: {str(e)}")
            return {'CANCELLED'}
        except json.JSONDecodeError as e:
            pass
            props.is_updating = False
            self.report({'ERROR'}, f"Invalid preset file format: {str(e)}")
            return {'CANCELLED'}
        except Exception as e:
            pass
            props.is_updating = False
            self.report({'ERROR'}, f"Unexpected error loading preset: {str(e)}")
            return {'CANCELLED'}
        
        return {'FINISHED'}

class TEXT_TEXTURE_OT_delete_preset(Operator):
    pass
    """Delete preset"""
    bl_idname = "text_texture.delete_preset"
    bl_label = "Delete Preset"
    bl_description = "Delete selected preset"
    bl_options = {'REGISTER', 'UNDO'}
    
    preset_name: StringProperty()
    
    def execute(self, context):
        pass
        props = context.scene.text_texture_props
        
        try:
            pass
            # Delete from both blend file and persistent storage
            blend_deleted = delete_blend_file_preset(self.preset_name)
            
            preset_file = os.path.join(get_persistent_preset_path(), f"{self.preset_name}.json")
            local_deleted = False
            if os.path.exists(preset_file):
                pass
                os.remove(preset_file)
                local_deleted = True
            
            if not blend_deleted and not local_deleted:
                pass
                self.report({'WARNING'}, f"Preset '{self.preset_name}' not found in blend file or local storage")
            
            # Remove from UI list
            removed_index = None
            for i, preset in enumerate(props.presets):
                pass
                if preset.name == self.preset_name:
                    pass
                    props.presets.remove(i)
                    removed_index = i
                    break
            
            if hasattr(props, "last_loaded_preset") and props.last_loaded_preset == self.preset_name:
                pass
                props.last_loaded_preset = ""
            if removed_index is not None and hasattr(props, "active_preset_index"):
                pass
                if len(props.presets) == 0:
                    pass
                    props.active_preset_index = 0
                else:
                    pass
                    props.active_preset_index = min(removed_index, len(props.presets) - 1)
            
            delete_info = []
            if blend_deleted:
                pass
                delete_info.append("blend file")
            if local_deleted:
                pass
                delete_info.append("local storage")
            
            location_text = " and ".join(delete_info) if delete_info else "nowhere (not found)"
            self.report({'INFO'}, f"🗑️ Deleted preset '{self.preset_name}' from {location_text}")
            
        except (OSError, IOError) as e:
            pass
            self.report({'ERROR'}, f"Failed to delete preset file: {str(e)}")
            return {'CANCELLED'}
        except Exception as e:
            pass
            self.report({'ERROR'}, f"Unexpected error deleting preset: {str(e)}")
            return {'CANCELLED'}
        
        return {'FINISHED'}
    
    def invoke(self, context, event):
        pass
        # Add confirmation dialog for better UX
        return context.window_manager.invoke_confirm(self, event)
    
    def draw(self, context):
        pass
        layout = self.layout
        layout.label(text=f"Delete preset '{self.preset_name}'?")
        layout.label(text="This action cannot be undone.", icon='ERROR')

class TEXT_TEXTURE_OT_refresh_presets(Operator):
    pass
    """Refresh presets from blend file and local storage"""
    bl_idname = "text_texture.refresh_presets"
    bl_label = "Refresh Presets"
    bl_description = "Reload presets from blend file and local storage"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        pass
        try:
            pass
            # Use the reliable synchronous refresh system
            success = refresh_presets_sync()
            
            if success:
                pass
                props = context.scene.text_texture_props
                total_presets = len(props.presets)
                
                # Count by source for detailed reporting
                blend_count = len([p for p in props.presets if p.source == 'BLEND_FILE'])
                persistent_count = len([p for p in props.presets if p.source == 'PERSISTENT_FILE'])
                local_count = len([p for p in props.presets if p.source == 'LOCAL_FILE'])
                
                self.report({'INFO'}, f"✅ Refreshed {total_presets} presets ({blend_count} blend file, {persistent_count} persistent, {local_count} local)")
            else:
                pass
                self.report({'WARNING'}, "Preset refresh completed with issues - check console for details")
            
            # Force UI redraw
            for area in context.screen.areas:
                pass
                area.tag_redraw()
            
            return {'FINISHED'}
            
        except Exception as e:
            pass
            self.report({'ERROR'}, f"Failed to refresh presets: {str(e)}")
            import traceback
            traceback.print_exc()
            return {'CANCELLED'}

class TEXT_TEXTURE_OT_export_presets(Operator):
    pass
    """Export all presets to a file for backup"""
    bl_idname = "text_texture.export_presets"
    bl_label = "Export Presets"
    bl_description = "Export all presets to a file for backup"
    bl_options = {'REGISTER', 'UNDO'}
    
    filepath: StringProperty(
        name="File Path",
        description="Path to save preset backup file",
        subtype='FILE_PATH',
        default="text_texture_presets_backup.json"
    )
    
    def execute(self, context):
        pass
        try:
            pass
            props = context.scene.text_texture_props
            
            # Collect all presets from all sources
            all_presets = {}
            
            # Get blend file presets
            blend_presets = get_blend_file_presets()
            for name, data in blend_presets.items():
                pass
                all_presets[name] = {
                    "data": data,
                    "source": "BLEND_FILE"
                }
            
            # Get persistent presets
            persistent_dir = get_persistent_preset_path()
            if os.path.exists(persistent_dir):
                pass
                for filename in os.listdir(persistent_dir):
                    pass
                    if filename.endswith('.json'):
                        pass
                        preset_name = os.path.splitext(filename)[0]
                        if preset_name not in all_presets:  # Don't override blend file presets
                            try:
                                pass
                                with open(os.path.join(persistent_dir, filename), 'r') as f:
                                    preset_data = json.load(f)
                                all_presets[preset_name] = {
                                    "data": preset_data,
                                    "source": "PERSISTENT_FILE"
                                }
                            except (json.JSONDecodeError, OSError) as e:
                                pass
            
            # Get legacy presets
            legacy_dir = get_preset_path()
            if os.path.exists(legacy_dir) and legacy_dir != persistent_dir:
                pass
                for filename in os.listdir(legacy_dir):
                    pass
                    if filename.endswith('.json'):
                        pass
                        preset_name = os.path.splitext(filename)[0]
                        if preset_name not in all_presets:  # Don't override higher priority presets
                            try:
                                pass
                                with open(os.path.join(legacy_dir, filename), 'r') as f:
                                    preset_data = json.load(f)
                                all_presets[preset_name] = {
                                    "data": preset_data,
                                    "source": "LOCAL_FILE"
                                }
                            except (json.JSONDecodeError, OSError) as e:
                                pass
            
            if not all_presets:
                pass
                self.report({'WARNING'}, "No presets found to export")
                return {'CANCELLED'}
            
            # Create export data with metadata
            export_data = {
                "format_version": "1.0",
                "addon_version": bl_info["version"],
                "export_timestamp": time.time(),
                "presets": all_presets
            }
            
            # Write to file
            with open(self.filepath, 'w') as f:
                json.dump(export_data, f, indent=2)
            
            self.report({'INFO'}, f"✅ Exported {len(all_presets)} presets to {os.path.basename(self.filepath)}")
            return {'FINISHED'}
            
        except Exception as e:
            pass
            self.report({'ERROR'}, f"Failed to export presets: {str(e)}")
            return {'CANCELLED'}
    
    def invoke(self, context, event):
        pass
        # Set default filename with timestamp
        import datetime
        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        self.filepath = f"text_texture_presets_backup_{timestamp}.json"
        
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

class TEXT_TEXTURE_OT_import_presets(Operator):
    pass
    """Import presets from a backup file"""
    bl_idname = "text_texture.import_presets"
    bl_label = "Import Presets"
    bl_description = "Import presets from a backup file"
    bl_options = {'REGISTER', 'UNDO'}
    
    filepath: StringProperty(
        name="File Path",
        description="Path to preset backup file",
        subtype='FILE_PATH'
    )
    
    import_mode: EnumProperty(
        name="Import Mode",
        description="How to handle existing presets with same names",
        items=[
            ('SKIP', 'Skip Existing', 'Skip presets that already exist'),
            ('OVERWRITE', 'Overwrite', 'Overwrite existing presets'),
            ('RENAME', 'Rename New', 'Rename imported presets if they conflict'),
        ],
        default='SKIP'
    )
    
    import_to_blend: BoolProperty(
        name="Import to Blend File",
        description="Import presets to blend file (recommended for portability)",
        default=True
    )
    
    def execute(self, context):
        pass
        try:
            pass
            if not os.path.exists(self.filepath):
                pass
                self.report({'ERROR'}, "Backup file not found")
                return {'CANCELLED'}
            
            props = context.scene.text_texture_props
            
            # Read backup file
            with open(self.filepath, 'r') as f:
                import_data = json.load(f)
            
            # Validate format
            if "presets" not in import_data:
                pass
                self.report({'ERROR'}, "Invalid backup file format")
                return {'CANCELLED'}
            
            imported_presets = import_data["presets"]
            if not imported_presets:
                pass
                self.report({'WARNING'}, "No presets found in backup file")
                return {'CANCELLED'}
            
            # Get existing presets to check for conflicts
            existing_blend_presets = get_blend_file_presets()
            existing_preset_names = set(existing_blend_presets.keys())
            
            # Also check persistent storage
            persistent_dir = get_persistent_preset_path()
            if os.path.exists(persistent_dir):
                pass
                for filename in os.listdir(persistent_dir):
                    pass
                    if filename.endswith('.json'):
                        pass
                        existing_preset_names.add(os.path.splitext(filename)[0])
            
            imported_count = 0
            skipped_count = 0
            errors = []
            
            for preset_name, preset_info in imported_presets.items():
                pass
                preset_data = preset_info.get("data", {})
                
                # Handle conflicts
                final_name = preset_name
                if preset_name in existing_preset_names:
                    pass
                    if self.import_mode == 'SKIP':
                        pass
                        skipped_count += 1
                        continue
                    elif self.import_mode == 'RENAME':
                        counter = 1
                        base_name = preset_name
                        while final_name in existing_preset_names:
                            pass
                            final_name = f"{base_name}_imported_{counter}"
                            counter += 1
                    # OVERWRITE mode uses original name
                
                try:
                    pass
                    # Import to blend file or persistent storage based on setting
                    if self.import_to_blend:
                        pass
                        success = save_blend_file_preset(final_name, preset_data)
                        if not success:
                            pass
                            raise Exception("Failed to save to blend file")
                    else:
                        pass
                        # Save to persistent storage
                        persistent_file = os.path.join(get_persistent_preset_path(), f"{final_name}.json")
                        with open(persistent_file, 'w') as f:
                            json.dump(preset_data, f, indent=2)
                    
                    imported_count += 1
                    
                    # Add to UI list if not already present
                    existing_preset = None
                    for preset in props.presets:
                        pass
                        if preset.name == final_name:
                            pass
                            existing_preset = preset
                            break
                    
                    if not existing_preset:
                        pass
                        new_preset = props.presets.add()
                        new_preset.name = final_name
                        new_preset.source = 'BLEND_FILE' if self.import_to_blend else 'PERSISTENT_FILE'
                    else:
                        pass
                        existing_preset.source = 'BLEND_FILE' if self.import_to_blend else 'PERSISTENT_FILE'
                    
                except Exception as e:
                    pass
                    errors.append(f"{preset_name}: {str(e)}")
            
            # Report results
            if imported_count > 0:
                pass
                storage_location = "blend file" if self.import_to_blend else "persistent storage"
                self.report({'INFO'}, f"✅ Imported {imported_count} presets to {storage_location}")
                if skipped_count > 0:
                    pass
                    self.report({'INFO'}, f"⏭️ Skipped {skipped_count} existing presets")
            else:
                pass
                self.report({'WARNING'}, "No presets were imported")
            
            if errors:
                pass
                self.report({'WARNING'}, f"Errors importing {len(errors)} presets - check console")
                for error in errors[:5]:  # Show first 5 errors
                    print(f"Import error: {error}")
            
            return {'FINISHED'}
            
        except Exception as e:
            pass
            self.report({'ERROR'}, f"Failed to import presets: {str(e)}")
            return {'CANCELLED'}
    
    def invoke(self, context, event):
        pass
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}
    
    def draw(self, context):
        pass
        layout = self.layout
        layout.prop(self, "import_mode")
        layout.prop(self, "import_to_blend")

# Export all operator classes for wildcard imports
__all__ = [
    'TEXT_TEXTURE_OT_save_preset',
    'TEXT_TEXTURE_OT_save_preset_enter',
    'TEXT_TEXTURE_OT_clear_preset_search',
    'TEXT_TEXTURE_OT_load_preset',
    'TEXT_TEXTURE_OT_delete_preset',
    'TEXT_TEXTURE_OT_refresh_presets',
    'TEXT_TEXTURE_OT_export_presets',
    'TEXT_TEXTURE_OT_import_presets',
]
