from .. import __package__ as base_package
import os
import bpy
import json
from bpy.types import Operator, PropertyGroup, AddonPreferences
from bpy.props import StringProperty, CollectionProperty, IntProperty, BoolProperty

def already_present(self, prefs, path, report=True):
    for p in prefs.projects:
        if p.path == path:
            if report:
                self.report(
                    {'WARNING'}, f'Current file is already in the templates list as "{p.name}".')
            return True
    return False

def name_from_path(path):
    if path:
        return os.path.splitext(os.path.basename(path))[0]
    else:
        return ""

def on_path_update(self, context):
    if not self.name and self.path:
        self.name = name_from_path(self.path)
        context.preferences.is_dirty = True
    if self.path and self.path.startswith('//'):
        self.path = bpy.path.abspath(self.path)
        context.preferences.is_dirty = True
    
class TemplateItem(PropertyGroup):
    name: StringProperty(
        name="Name", description="Display name for this template")
    path: StringProperty(
        name="Path", description="Path to the .blend file for this template", subtype='FILE_PATH', update=on_path_update)

class CustomTemplatesPreferences(AddonPreferences):
    bl_idname = base_package

    override_splash: BoolProperty(
        default=True, name="Override Splash Screen Templates", description="Override Splash Screen's 'New File' list with your Custom Templates")
    projects: CollectionProperty(type=TemplateItem)
    active_template_index: IntProperty(
        description="Index of the selected template")

    def draw(self, context):
        layout = self.layout

        layout.label(
            text=f"Your custom templates ({len(self.projects)})")
        row = layout.row()
        row.template_list("UI_UL_list", "custom_templates",
                          self, "projects", self, "active_template_index", rows=6, maxrows=6)

        col = row.column(align=True)
        col.menu("CT_MT_templates_menu", icon='DOWNARROW_HLT', text="")
        col.separator()
        col.operator("ct.add_templates_from_folder", text="", icon="FILE_FOLDER")
        col.operator("ct.add", icon='ADD', text="")
        col.operator("ct.remove", icon='REMOVE', text="")
        col.separator()
        col.operator("ct.move_up", icon='TRIA_UP', text="")
        col.operator("ct.move_down", icon='TRIA_DOWN', text="")

        if self.projects:
            project = self.projects[self.active_template_index]
            layout.prop(project, "path")
            layout.prop(project, "name")

        layout.prop(self, "override_splash")
        box = layout.box()
        if self.override_splash and len(self.projects) == 0:
            box.label(text="There are currently no templates.")
        elif self.override_splash:
            box.label(text="Custom templates will be shown in the Splash Screen.")

        if not self.override_splash or len(self.projects) == 0:
            box.label(
                text="The default Blender list will be shown in the Splash Screen.")

        if len(self.projects) > 5:
            box.label(
                text="Note: Only the first 5 templates in the list will be shown in the splash screen.")
        
class CT_MT_templates_menu(bpy.types.Menu):
    bl_label = "Manage your custom templates"
    bl_description = "Import, export, add from folder (with controllable recursion depth), clear current templates"

    def draw(self, context):
        layout = self.layout
        layout.operator("ct.add_templates_from_folder", text="Add from folder", icon="ADD")
        layout.operator("ct.clear", text="Clear current templates", icon="TRASH")
        layout.separator()
        layout.operator("ct.import_templates", text="Import templates", icon="IMPORT")
        layout.operator("ct.export_templates", text="Export templates", icon="EXPORT")

class CT_OT_export_templates(bpy.types.Operator):
    bl_idname = "ct.export_templates"
    bl_label = "Export custom templates"
    bl_description = "Export the current list of templates to JSON file"

    filepath: StringProperty(
        subtype="FILE_PATH", description="Select the path for the exported file", default="templates.json")

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences
        with open(self.filepath, 'w') as f:
            projects = [{"name": project.name, "path": project.path}
                        for project in prefs.projects]
            json.dump(projects, f, indent=4)

        self.report({'INFO'}, f"Templates exported to {self.filepath}")
        return {'FINISHED'}

    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

    @classmethod
    def poll(cls, context):
        return len(context.preferences.addons[base_package].preferences.projects) > 0

class CT_OT_import_templates(bpy.types.Operator):
    bl_idname = "ct.import_templates"
    bl_label = "Import custom templates"
    bl_description = "Import a list of templates from JSON file (note that this will override the current templates list)"

    filepath: StringProperty(
        subtype="FILE_PATH", description="Select the .json file to load")

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences

        if os.path.exists(self.filepath):
            with open(self.filepath, 'r') as f:
                projects = json.load(f)

            prefs.projects.clear()
            for project in projects:
                item = prefs.projects.add()
                item.name = project["name"]
                item.path = project["path"]
            prefs.active_template_index = 0
            context.preferences.is_dirty = True

            self.report({'INFO'}, f"Projects imported from {self.filepath}")
        else:
            self.report(
                {'WARNING'}, f"Import cancelled: path not found ({self.filepath})")
        return {'FINISHED'}

    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

class CT_OT_add(Operator):
    bl_idname = "ct.add"
    bl_label = "Add Template"
    bl_description = "Add new template"

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences
        prefs.projects.add()
        prefs.active_template_index = len(prefs.projects) - 1
        context.preferences.is_dirty = True
        return {'FINISHED'}

class CT_OT_remove(Operator):
    bl_idname = "ct.remove"
    bl_label = "Remove Template"
    bl_description = "Remove selected template"

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences
        prefs.projects.remove(prefs.active_template_index)
        prefs.active_template_index = min(
            max(0, prefs.active_template_index - 1), len(prefs.projects) - 1)
        context.preferences.is_dirty = True
        return {'FINISHED'}

    @classmethod
    def poll(cls, context):
        return len(context.preferences.addons[base_package].preferences.projects) > 0

class CT_OT_move_up(Operator):
    bl_idname = "ct.move_up"
    bl_label = "Move Up"
    bl_description = "Move the selected template up in the list"

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences
        index = prefs.active_template_index

        if index > 0:
            prefs.projects.move(index, index - 1)
            prefs.active_template_index -= 1
            context.preferences.is_dirty = True
        else:
            self.report({'WARNING'}, "Template is already at the top")
        return {'FINISHED'}

    @classmethod
    def poll(cls, context):
        return len(context.preferences.addons[base_package].preferences.projects) > 0

class CT_OT_move_down(Operator):
    bl_idname = "ct.move_down"
    bl_label = "Move Down"
    bl_description = "Move the selected template down in the list"

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences
        index = prefs.active_template_index

        if index < len(prefs.projects) - 1:
            prefs.projects.move(index, index + 1)
            prefs.active_template_index += 1
            context.preferences.is_dirty = True
        else:
            self.report({'WARNING'}, "Template is already at the bottom")
        return {'FINISHED'}

    @classmethod
    def poll(cls, context):
        return len(context.preferences.addons[base_package].preferences.projects) > 0

class CT_OT_clear(Operator):
    bl_idname = "ct.clear"
    bl_label = "Clear Templates"
    bl_description = "Clear the current list of templates (remove all templates)"

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences
        prefs.projects.clear()
        prefs.active_template_index = 0
        context.preferences.is_dirty = True
        return {'FINISHED'}
    
    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)

    @classmethod
    def poll(cls, context):
        return len(context.preferences.addons[base_package].preferences.projects) > 0

class CT_OT_add_template_popup(Operator):
    bl_idname = "ct.add_template_popup"
    bl_label = "Use as template"
    bl_description = "Use the current .blend file to create a new template occurency"

    name: StringProperty(name="Project Name")

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences
        if not already_present(self, prefs, bpy.data.filepath):
            if bpy.data.filepath:
                new_project = prefs.projects.add()
                new_project.name = self.name
                new_project.path = bpy.data.filepath
                self.name = ''
                context.preferences.is_dirty = True
            else:
                self.report({'ERROR'}, "Current file is not saved on disk.")
        return {'FINISHED'}

    def invoke(self, context, event):
        if bpy.data.filepath:
            self.name = name_from_path(bpy.data.filepath)
        return context.window_manager.invoke_props_dialog(self)

class CT_OT_select_template_popup(Operator):
    bl_idname = "ct.select_template_popup"
    bl_label = "Select a new custom template"
    bl_description = "Create a new template by selecting an existing .blend file"

    path: StringProperty(name="Template Path", subtype="FILE_PATH", update=on_path_update)
    name: StringProperty(name="Template Name")

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences
        if not already_present(self, prefs, self.path):
            template = prefs.projects.add()
            template.name = self.name
            template.path = self.path
            context.preferences.is_dirty = True
            self.name = ''
            self.path = ''
            
        return {'FINISHED'}

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)

class CT_OT_add_templates_from_folder(bpy.types.Operator):
    bl_idname = "ct.add_templates_from_folder"
    bl_label = "Add Templates from Folder"
    bl_description = "Add templates from a specified folder containing .blend files"

    directory: StringProperty(
        subtype="DIR_PATH", description="Select the folder containing .blend files")
    depth: IntProperty(
        name="Depth", description="Depth of recursion (default 1)", default=1, min=1)

    def execute(self, context):
        prefs = context.preferences.addons[base_package].preferences

        if os.path.exists(self.directory):
            blend_files = []
            for root, dirs, files in os.walk(self.directory):
                # Calculate the current depth
                current_depth = root[len(self.directory):].count(os.sep)
                if current_depth < self.depth:
                    for file in files:
                        if file.endswith('.blend'):
                            blend_files.append(os.path.join(root, file))

            if not blend_files:
                self.report(
                    {'WARNING'}, "No .blend files found in the specified directory")
                return {'CANCELLED'}
            new_t = 0
            for path in blend_files:
                if not already_present(self, prefs, path, False):
                    new_t += 1
                    item = prefs.projects.add()
                    item.name = name_from_path(path)
                    item.path = path

            context.preferences.is_dirty = True
            self.report(
                {'INFO'}, f"{new_t} new templates added from {self.directory} ({len(blend_files) - new_t} already present)")
        else:
            self.report(
                {'WARNING'}, f"Import cancelled: directory not found ({self.directory})")
        return {'FINISHED'}

    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

class CT_OT_open_preferences(Operator):
    bl_idname = "ct.open_preferences"
    bl_label = "Open Custom Templates Preferences"
    bl_description = "Open the preferences for the Custom Templates add-on"

    def execute(self, context):
        bpy.ops.screen.userpref_show('INVOKE_DEFAULT')
        context.preferences.active_section = 'ADDONS'
        context.window_manager.addon_search = "Custom Templates"
        return {'FINISHED'}