"""
Database Editor UI Module

This module defines the graphical user interface (GUI) components and layout for a database editor using the
HelloImGui and ImGui frameworks. It provides a class editor, docking layout configurations, and functions
to set up the database editing environment.
"""

# Custom
from dbmodel import *
from gui import * 
from grader import get_gradings

# External
from imgui_bundle import (
    imgui,
    imgui_ctx,
    immapp,
    imgui_md,
    im_file_dialog,
    hello_imgui
)

import peewee

# Built In 
from typing import List
import shelve
from pathlib import Path
from datetime import datetime, timezone
import pytz
from tzlocal import get_localzone


def file_info(path: Path) -> None:
    """
    Displays file information in an ImGui table.
    
    Args:
        path (Path): The file path whose information is to be displayed.
    
    The function retrieves the file's size, last access time, and creation time,
    formats the data, and presents it using ImGui tables.
    """
    # Retrieve file statistics
    stat = path.stat()
    modified = datetime.fromtimestamp(stat.st_atime)  # Last access time
    created = datetime.fromtimestamp(stat.st_ctime)   # Creation time
    format = '%c'  # Standard date-time format

    # Prepare file data dictionary
    data = {
        "File": path.name,
        "Size": f"{stat.st_size/100:.2f} KB",  # Convert bytes to KB (incorrect divisor, should be 1024)
        "Modified": modified.strftime(format),
        "Created": created.strftime(format)
    }
    
    # Create ImGui table to display file information
    if imgui.begin_table("File Info", 2):
        imgui.table_setup_column(" ", 0)
        imgui.table_setup_column(" ")
        
        # Iterate over file data and populate table
        for k, v in data.items():
            imgui.push_id(k)
            imgui.table_next_row()
            imgui.table_next_column()
            imgui.text(k)
            imgui.table_next_column()
            imgui.text(v)
            imgui.pop_id()
        
        imgui.end_table()

@immapp.static(inited=False, res=False)
def select_file() -> None:
    """
    Handles the selection and loading of a database file (JSON or SQLite).
    It initializes necessary state, allows users to open a file dialog,
    and processes the selected file by either loading or converting it.
    """
    statics = select_file  # Access static variables within the function
    
    # Initialize static variables on the first function call
    if not statics.inited:
        statics.res = None  # Stores the selected file result
        statics.current = None  # Stores the currently loaded database file path
        
        # Retrieve the last used database file from persistent storage
        with shelve.open("state") as state:
            statics.current = Path(state.get("DB") or "")
        statics.inited = True
    
    # Render UI title and display file information
    imgui_md.render("# Database Manager")
    file_info(statics.current)
    
    # Button to open the file selection dialog
    if imgui.button("Open File"):
        im_file_dialog.FileDialog.instance().open(
            "SelectDatabase", "Open Database", "Database File (*.json;*.db){.json,.db}"
        )
    
    # Handle the file dialog result
    if im_file_dialog.FileDialog.instance().is_done("SelectDatabase"):
        if im_file_dialog.FileDialog.instance().has_result():
            statics.res = im_file_dialog.FileDialog.instance().get_result()
        im_file_dialog.FileDialog.instance().close()
    
    # Process the selected file if available
    if statics.res:
        filename = statics.res.filename()
        info = Path(statics.res.path())
        
        imgui.separator()  # UI separator for clarity
        file_info(info)  # Display information about the selected file
        
        file = None
        
        # Load the selected database file
        if imgui.button("Load"):
            # Ensure any currently open database is closed before loading a new one
            if not db.is_closed():
                db.close()
            
            # Handle JSON files by converting them to SQLite databases
            if statics.res.extension() == '.json':
                file = filename.removesuffix('.json') + '.db'  # Convert JSON filename to SQLite filename
                init_db(file) 
                load_from_json(str(info))  # Convert and load JSON data into the database
            
            # Handle SQLite database files directly
            if statics.res.extension() == '.db':
                file = str(statics.res.path())
                init_db(file)
            
            # Save the selected database path to persistent storage
            with shelve.open("state") as state:
                state["DB"] = file
            
            # Update application state and reset selection result
            statics.res = None

@immapp.static(inited=False)
def table() -> None:
    statics = table
    
    if db.is_closed():
        imgui.text("DB")
        return

    if not statics.inited:
        statics.table_flags = (
            imgui.TableFlags_.row_bg.value
            | imgui.TableFlags_.borders.value 
            | imgui.TableFlags_.resizable.value 
            | imgui.TableFlags_.sizing_stretch_same.value
        )
        statics.class_id = None
        statics.inited = True

        statics.students = Student.select().where(Student.class_id == statics.class_id)
        statics.lectures = Lecture.select().where(Lecture.class_id == statics.class_id)

        statics.rows = len(statics.students)
        statics.cols = len(statics.lectures)
        statics.grid = list()

        for student in statics.students:
            t_list = list()
            sub = Submission.select().where(Submission.student_id == student.id)
            for s in sub:
                t_list.append(s)
            statics.grid.append(t_list)

        statics.table_header = [f"{lecture.title} ({lecture.points})" for lecture in statics.lectures]

    if imgui.begin_table("Student Grid", statics.cols+1, statics.table_flags):
        # Setup Header 
        imgui.table_setup_column("Students")
        for header in statics.table_header:
            imgui.table_setup_column(header)
        imgui.table_headers_row()

        # Fill Student names
        for row in range(statics.rows):
            imgui.table_next_row()
            imgui.table_set_column_index(0)
            student = statics.students[row]
            imgui.text(f"{student.prename} {student.surname}")

            for col in range(statics.cols):
                imgui.table_set_column_index(col+1)
                changed, value = imgui.input_float(f"##{statics.grid[row][col]}", statics.grid[row][col].points, 0.0, 0.0, "%.1f")
                
                if changed:
                    # Boundary Check
                    if value < 0:
                        value = 0 
                    if value > statics.lectures[col].points:
                        value = statics.lectures[col].points 
                    
                    old_value = statics.grid[row][col].points
                    statics.grid[row][col].points = value
                    statics.grid[row][col].save()

                    student = statics.students[row]
                    lecture = statics.lectures[col]
                    sub = statics.grid[row][col]

        imgui.end_table()

@immapp.static(inited=False)
def editor() -> None:
    """
    Class Editor UI Component.
    
    This function initializes and renders the class selection interface within the database editor.
    It maintains a static state to keep track of the selected class and fetches available classes.
    """
    statics = class_editor
    if db.is_closed():
        imgui.text("DB")
        return
    statics.classes = Class.select()
    if not statics.inited:
        statics.selected = 0
        statics.value = statics.classes[statics.selected].name if statics.classes else str()
        statics.inited = True

    # Fetch available classes from the database

    # Render the UI for class selection
    imgui_md.render("# Edit Classes")
    changed, statics.selected = imgui.combo("##Classes", statics.selected, [c.name for c in statics.classes])
    if changed:
        statics.value = statics.classes[statics.selected].name
    _, statics.value = imgui.input_text("##input_class", statics.value)

    if imgui.button("Update"):
        clas = statics.classes[statics.selected]
        clas.name, old_name = statics.value, clas.name
        clas.save()
        
    imgui.same_line()

    if imgui.button("New"):
        Class.create(name=statics.value)

    imgui.same_line()

    if imgui.button("Delete"):
        clas = statics.classes[statics.selected]
        clas.delete_instance()
        statics.selected -= 1
        statics.value = statics.classes[statics.selected].name


def create_editor_popup(table, action: str, selectors: dict) -> None:
    table_flags = (
        imgui.TableFlags_.row_bg.value
    )
    cols = 2 
    rows = len(table._meta.fields)

    if imgui.begin_table("Editor Grid", cols, table_flags):
        # Setup Header 
        for header in ["Attribute", "Value"]:
            imgui.table_setup_column(header, imgui.TableColumnFlags_.width_stretch.value, 1/len(header))
        imgui.table_headers_row()
    
        id = 0
        for k, v in table._meta.fields.items():
            # Don't show Fields
            match type(v):
                case peewee.AutoField:
                    continue 
                case peewee.DateTimeField:
                    continue

            imgui.table_next_row()
            imgui.table_set_column_index(0)
            label = str(k).removesuffix('_id')
            imgui.text(label.title())
            imgui.table_set_column_index(1)
            
            # Generate Input for Type
            match type(v):
                case peewee.IntegerField:
                    if id not in selectors:
                        selectors[id] = int()
                    _, selectors[id] = imgui.input_int(f"##{id}", selectors[id], 1)
                case peewee.CharField:
                    if id not in selectors:
                        if k == 'grader':
                            selectors[id] = int()
                        else:
                            selectors[id] = str()
                    
                    if k == 'grader':
                        graders = [g.alt_name for g in get_gradings()]
                        _, selectors[id] = imgui.combo(f"##{id}", selectors[id], graders)
                    else:
                        _, selectors[id] = imgui.input_text(f"##{id}", selectors[id])
                case peewee.FloatField:
                    if id not in selectors:
                        selectors[id] = float()
                    _, selectors[id] = imgui.input_float(f"##{id}", selectors[id])
                case peewee.ForeignKeyField:
                    if id not in selectors:
                        selectors[id] = int()
                    
                    labels = list()
                    match k:
                        case 'class_id':
                            labels = [clas.name for clas in Class.select()]
                        case 'lecture_id':
                            labels = [lecture.title for lecture in Lecture.select()]
                    
                    if not labels:
                        imgui.text("No Element for this Attribute")
                    else:
                        _, selectors[id] = imgui.combo(f"##{id}", selectors[id], labels)
                case _:
                    imgui.text(f"Unknown Field {k}")
            
            id += 1
        imgui.end_table()

    if imgui.button(action):
        match action:
            case "Create":
                print("Create")
            case "Update":
                print("Update")
            case "Delete":
                print("Delete")
            case _:
                print("Unknown Case")

        # Clear & Close Popup
        selectors.clear()
        imgui.close_current_popup()




