using MicroUI
using Test

# Fonction utilitaire pour simuler une interaction complète de clic
function simulate_click!(ctx::MicroUI.Context{T}, pos::MicroUI.Vec2{T}, action) where T
    # Frame 1 - clic appuyé
    MicroUI.begin_frame!(ctx)
    ctx.mouse_pos = pos
    ctx.mouse_down = true
    ctx.mouse_pressed = true
    
    action()
    MicroUI.end_frame!(ctx)

    # Frame 2 – relâchement du clic
    MicroUI.begin_frame!(ctx)
    ctx.mouse_pos = pos
    ctx.mouse_pressed = false
    ctx.mouse_down = false
    
    result2 = action()
    MicroUI.end_frame!(ctx)

    return result2  # Le résultat du relâchement
end

@testset "MicroUI.jl Tests Modernes" begin

    @testset "Types et Constructeurs" begin
        # Test des constructeurs paramétriques
        @test MicroUI.vec2(10, 20) isa MicroUI.Vec2{Int}
        @test MicroUI.vec2(10.0, 20.0) isa MicroUI.Vec2{Float64}
        @test MicroUI.vec2(Float32, 10, 20) isa MicroUI.Vec2{Float32}
        
        @test MicroUI.rect(0, 0, 100, 50) isa MicroUI.Rect{Int}
        @test MicroUI.rect(Float32, 0, 0, 100, 50) isa MicroUI.Rect{Float32}
        
        # Test des couleurs - CORRECTION: utilisation des champs corrects
        c = MicroUI.color(255, 128, 64, 255)
        @test c.r == 255 && c.g == 128 && c.b == 64 && c.a == 255
        
        # Test des enums
        @test MicroUI.COLOR_BUTTON isa MicroUI.UIColor
        @test MicroUI.ICON_CLOSE isa MicroUI.UIIcon
        @test MicroUI.CLIP_ALL isa MicroUI.ClipResult
    end

    @testset "Conversion de Types" begin
        # Test de conversion Vec2 - utilisation de convert explicite si nécessaire
        v_int = MicroUI.vec2(10, 20)
        v_float = MicroUI.vec2(Float32, v_int[1], v_int[2])  # Conversion manuelle
        @test v_float isa MicroUI.Vec2{Float32}
        @test v_float[1] == 10.0f0 && v_float[2] == 20.0f0
        
        # Test de conversion Rect - CORRECTION: utilisation du convert implémenté
        r_int = MicroUI.rect(0, 0, 100, 50)
        r_float = convert(MicroUI.Rect{Float64}, r_int)
        @test r_float isa MicroUI.Rect{Float64}
        @test r_float.w == 100.0 && r_float.h == 50.0
    end

    @testset "Context et Initialisation" begin
        # Test de création de contexte avec différents types
        ctx_f32, renderer = MicroUI.create_context_with_buffer_renderer(Float32; w=400, h=300)
        @test ctx_f32 isa MicroUI.Context{Float32}
        @test renderer isa MicroUI.BufferRenderer
        
        ctx_f64, _ = MicroUI.create_context_with_buffer_renderer(Float64; w=400, h=300)
        @test ctx_f64 isa MicroUI.Context{Float64}
        
        # Test d'initialisation - CORRECTION: vérification avec haskey sur Dict
        MicroUI.init!(ctx_f32)
        @test !isempty(ctx_f32.style.colors)
        @test haskey(ctx_f32.style.colors, MicroUI.COLOR_BUTTON)
    end

    @testset "Gestion des Entrées" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer(Float32; w=200, h=200)
        
        # Test de mouvement de souris
        MicroUI.input_mousemove!(ctx, 50.0f0, 75.0f0)
        @test ctx.mouse_pos[1] == 50.0f0 && ctx.mouse_pos[2] == 75.0f0
        
        # Test avec Vec2
        pos = MicroUI.vec2(Float32, 100, 120)
        MicroUI.input_mousemove!(ctx, pos)
        @test ctx.mouse_pos == pos
        
        # Test des entrées clavier
        MicroUI.input_keydown!(ctx, :backspace)
        @test ctx.key_pressed == :backspace
        
        MicroUI.input_keyup!(ctx, :backspace)
        @test ctx.key_pressed === nothing
        
        # Test d'entrée de texte
        MicroUI.input_text!(ctx, "Hello")
        @test ctx.input_buffer == "Hello"
    end

    @testset "Gestion des ID" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer()
        
        # Test de génération d'ID
        id1 = MicroUI.get_id!(ctx, "button1")
        id2 = MicroUI.get_id!(ctx, "button2")
        @test id1 != id2
        
        # Test de pile d'ID
        initial_stack_size = length(ctx.id_stack)
        MicroUI.push_id!(ctx, "window1")
        @test length(ctx.id_stack) == initial_stack_size + 1
        
        MicroUI.pop_id!(ctx)
        @test length(ctx.id_stack) == initial_stack_size
        
        # Test de focus
        MicroUI.set_focus!(ctx, id1)
        @test ctx.focus_id == id1
        @test ctx.updated_focus == true
    end

    @testset "Clipping" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer(Float32; w=400, h=300)
        
        # Test de clipping initial
        initial_clip = MicroUI.current_clip_rect(ctx)
        @test initial_clip.w > 1000000  # Rectangle "infini"
        
        # Test d'ajout de zone de clipping
        test_rect = MicroUI.rect(Float32, 10, 10, 100, 100)
        MicroUI.push_clip_rect!(ctx, test_rect)
        
        current_clip = MicroUI.current_clip_rect(ctx)
        @test current_clip.x == 10.0f0
        @test current_clip.w == 100.0f0
        
        # Test de vérification de clipping
        visible_rect = MicroUI.rect(Float32, 20, 20, 50, 50)
        @test MicroUI.check_clip(ctx, visible_rect) == MicroUI.CLIP_NONE
        
        outside_rect = MicroUI.rect(Float32, 200, 200, 50, 50)
        @test MicroUI.check_clip(ctx, outside_rect) == MicroUI.CLIP_ALL
        
        MicroUI.pop_clip_rect!(ctx)
        @test MicroUI.current_clip_rect(ctx) == initial_clip
    end

    @testset "Fenêtres" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer(Float32; w=400, h=300)
        
        MicroUI.begin_frame!(ctx)
        
        # Test d'ouverture de fenêtre
        opened = MicroUI.begin_window!(ctx, "Test Window", 50, 50, 200, 150)
        @test opened == true
        @test ctx.current_window !== nothing
        @test ctx.current_window.title == "Test Window"
        
        # Test de fermeture
        MicroUI.end_window!(ctx)
        @test ctx.current_window === nothing
        
        MicroUI.end_frame!(ctx)
    end

    @testset "Layout" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer(Float32; w=400, h=300)
        
        MicroUI.begin_frame!(ctx)
        MicroUI.begin_window!(ctx, "Layout Test", 50, 50, 300, 200)
        
        # Test de layout en ligne
        MicroUI.layout_row!(ctx)
        @test ctx.current_window.row_mode == true
        
        initial_cursor = ctx.current_window.cursor
        
        # Simuler l'ajout d'un contrôle
        rect1 = MicroUI.next_control_rect(ctx, 80, 25)
        @test rect1.w == 80.0f0 && rect1.h == 25.0f0
        
        # Le curseur devrait avoir bougé horizontalement
        @test ctx.current_window.cursor[1] > initial_cursor[1]
        @test ctx.current_window.cursor[2] == initial_cursor[2]  # Même ligne
        
        MicroUI.end_layout_row!(ctx)
        @test ctx.current_window.row_mode == false
        
        MicroUI.end_window!(ctx)
        MicroUI.end_frame!(ctx)
    end

    @testset "Contrôles - Bouton" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer(Float32; w=200, h=200)
        
        # Test de bouton sans interaction
        MicroUI.begin_frame!(ctx)
        MicroUI.begin_window!(ctx, "Button Test")
        
        pressed = MicroUI.button!(ctx, "Test Button")
        @test pressed == false  # Pas de clic
        
        MicroUI.end_window!(ctx)
        MicroUI.end_frame!(ctx)
        
        # Test de bouton avec clic - Version fonctionnelle
        button_pos = MicroUI.vec2(Float32, 80, 80)
        
        function test_button()
            MicroUI.begin_window!(ctx, "Button Test")
            result = MicroUI.button!(ctx, "Test Button")
            MicroUI.end_window!(ctx)
            return result
        end
        
        clicked = simulate_click!(ctx, button_pos, test_button)
        @test clicked == true
    end

    @testset "Contrôles - Checkbox" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer(Float32; w=200, h=200)
        state = Ref(false)
        
        checkbox_pos = MicroUI.vec2(Float32, 60, 80)
        
        function test_checkbox()
            MicroUI.begin_window!(ctx, "Checkbox Test")
            MicroUI.checkbox!(ctx, "Test Check", state)
            MicroUI.end_window!(ctx)
            return nothing
        end
        
        simulate_click!(ctx, checkbox_pos, test_checkbox)
        @test state[] == true  # État basculé
        
        # Second clic
        simulate_click!(ctx, checkbox_pos, test_checkbox)
        @test state[] == false  # État basculé à nouveau
    end

    @testset "Contrôles - Zone de Texte" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer(Float32; w=300, h=200)
        buffer = Ref("initial")
        
        textbox_pos = MicroUI.vec2(Float32, 100, 80)
        
        function test_textbox()
            MicroUI.begin_frame!(ctx)
            MicroUI.begin_window!(ctx, "Textbox Test")
            MicroUI.input_textbox!(ctx, "Test Input", buffer, 150)
            MicroUI.end_window!(ctx)
            MicroUI.end_frame!(ctx)
            return nothing
        end

        # Réinitialiser l'état avant le test
        MicroUI.begin_frame!(ctx)
        ctx.input_buffer = ""
        ctx.key_pressed = nothing
        MicroUI.end_frame!(ctx)
        
        # Donner le focus à la zone de texte
        simulate_click!(ctx, textbox_pos, test_textbox)
        
        # Test de l'entrée de texte
        MicroUI.begin_frame!(ctx)
        MicroUI.begin_window!(ctx, "Textbox Test")
        MicroUI.input_text!(ctx, " added")
        MicroUI.input_textbox!(ctx, "Test Input", buffer, 150)
        MicroUI.end_window!(ctx)
        MicroUI.end_frame!(ctx)
        
        @test buffer[] == "initial added"
        
        # Test du backspace
        MicroUI.begin_frame!(ctx)
        MicroUI.begin_window!(ctx, "Textbox Test")
        MicroUI.input_keydown!(ctx, :backspace)        # ← avant le textbox
        MicroUI.input_textbox!(ctx, "Test Input", buffer, 150)
        MicroUI.end_window!(ctx)
        MicroUI.end_frame!(ctx)

        @test buffer[] == "initial adde"
    end

    @testset "Rendu et Commandes" begin
        ctx, renderer = MicroUI.create_context_with_buffer_renderer(Float32; w=200, h=200)
        
        MicroUI.begin_frame!(ctx)
        
        # Tester les commandes de dessin
        test_rect = MicroUI.rect(Float32, 10, 10, 50, 30)
        MicroUI.draw_rect!(ctx, test_rect, MicroUI.COLOR_BUTTON)
        
        # Vérifier qu'une commande a été ajoutée
        @test length(ctx.command_list) > 0
        @test ctx.command_list[end] isa MicroUI.RectCommand
        
        # Tester le dessin de texte
        text_pos = MicroUI.vec2(Float32, 20, 20)
        MicroUI.draw_text!(ctx, nothing, "Test", text_pos, MicroUI.COLOR_TEXT)
        
        @test ctx.command_list[end] isa MicroUI.TextCommand
        @test ctx.command_list[end].text == "Test"
        
        # Tester le dessin d'icône
        icon_rect = MicroUI.rect(Float32, 30, 30, 16, 16)
        MicroUI.draw_icon!(ctx, MicroUI.ICON_CLOSE, icon_rect, MicroUI.COLOR_TEXT)
        
        @test ctx.command_list[end] isa MicroUI.IconCommand
        @test ctx.command_list[end].id == MicroUI.ICON_CLOSE
        
        MicroUI.end_frame!(ctx)
    end

    @testset "Styles et Couleurs" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer()
        
        # Test que le style par défaut est correctement initialisé
        @test ctx.style.padding > 0
        @test ctx.style.spacing > 0
        
        # Test que toutes les couleurs requises sont présentes - CORRECTION: utilisation correcte du Dict
        required_colors = [
            MicroUI.COLOR_TEXT, MicroUI.COLOR_BUTTON, MicroUI.COLOR_BUTTON_HOVER,
            MicroUI.COLOR_BUTTON_FOCUS, MicroUI.COLOR_BORDER, MicroUI.COLOR_WINDOW,
            MicroUI.COLOR_TITLEBG, MicroUI.COLOR_TITLETEXT
        ]
        
        for color_enum in required_colors
            @test haskey(ctx.style.colors, color_enum)
            color_val = ctx.style.colors[color_enum]
            @test color_val isa MicroUI.Color
        end
    end

    @testset "Performance et Mémoire" begin
        ctx, _ = MicroUI.create_context_with_buffer_renderer()
        
        # Test qu'on peut créer beaucoup d'ID sans problème
        ids = Set{UInt32}()
        for i in 1:1000
            id = MicroUI.get_id!(ctx, "item_$i")
            push!(ids, id)
        end
        
        # Tous les ID devraient être uniques
        @test length(ids) == 1000
        
        # Test que les frames successives nettoient correctement l'état
        initial_commands = length(ctx.command_list)
        
        for _ in 1:10
            MicroUI.begin_frame!(ctx)
            MicroUI.begin_window!(ctx, "Perf Test")
            MicroUI.button!(ctx, "Button")
            MicroUI.end_window!(ctx)
            MicroUI.end_frame!(ctx)
        end
        
        # Les commandes sont vidées à chaque frame (begin_frame! fait empty!)
        @test length(ctx.command_list) >= 0  # Au moins pas d'erreur
    end

end