package gui

import clay "clay:."
import "core:fmt"
import "core:strings"
import rl "vendor:raylib"
import "../core"
import "../shared"
import "../ui"

// GUI_App_State holds all mutable GUI-local state for the main loop.
GUI_App_State :: struct {
	controller:       ui.App_Controller,
	selected_field:   int, // 0 idea, 1 genre, 2 audience, 3 export_path, 4 pages, 5 project_path, 6 autosave_interval
	export_path:      string,
	project_path:     string,
	local_script_pages: string,
	autosave_interval_text: string,
	export_format:    core.Export_Format,
	use_fal_panels:      bool,
	status_msg:      string,
	is_dirty:        bool,
	autosave_enabled: bool,
	autosave_interval_s: f64,
	last_autosave_at: f64,
	last_save_at:    f64,
	last_export_at:  f64,
	action_log:      Action_Log,
	log_show_lines:  i32,
	log_oldest_first: bool,
	summary_opts:    Summary_View_Options,
	show_help_overlay: bool,
	show_confirm_overlay: bool,
	pending_confirm: Pending_Confirm_Action,
	panel_textures:   map[string]rl.Texture2D,
	bubble_edit_text:  string,
}

clicked :: proc(id: clay.ElementId) -> bool {
	return clay.PointerOver(id) && rl.IsMouseButtonPressed(.LEFT)
}

// ─── Panel Image Loading ─────────────────────────────────────────
@(private)
file_url_to_path :: proc(url: string) -> string {
	if strings.has_prefix(url, "file://") {
		return url[7:]
	}
	return url
}

@(private)
load_panel_texture :: proc(cache: ^map[string]rl.Texture2D, panel_id: string, url: string) -> (rl.Texture2D, bool) {
	if tex, ok := cache[panel_id]; ok {
		return tex, true
	}
	filepath := file_url_to_path(url)
	if len(filepath) == 0 { return {}, false }
	fpath_c := strings.clone_to_cstring(filepath)
	defer delete(fpath_c)
	img := rl.LoadImage(fpath_c)
	if img.data == nil { return {}, false }
	defer rl.UnloadImage(img)
	tex := rl.LoadTextureFromImage(img)
	if tex.id == 0 { return {}, false }
	cache[panel_id] = tex
	return tex, true
}

@(private)
unload_panel_textures :: proc(cache: ^map[string]rl.Texture2D) {
	for _, tex in cache {
		rl.UnloadTexture(tex)
	}
	delete(cache^)
}

run_gui_app :: proc(state: ^core.Comic_State) -> shared.App_Error {
	controller := ui.new_controller(state^)
	defer ui.dispose_job_manager(&controller.jobs)

	rl.SetConfigFlags({.WINDOW_RESIZABLE, .WINDOW_UNDECORATED})
	rl.InitWindow(1240, 820, "comic-odin gui")
	defer rl.CloseWindow()
	monitor := rl.GetCurrentMonitor()
	monitor_pos := rl.GetMonitorPosition(monitor)
	rl.SetWindowPosition(i32(monitor_pos.x), i32(monitor_pos.y))
	rl.SetWindowSize(rl.GetMonitorWidth(monitor), rl.GetMonitorHeight(monitor))
	rl.SetWindowState({.BORDERLESS_WINDOWED_MODE})
	rl.SetTargetFPS(60)

	clay_init(1240, 820)
	defer clay_shutdown()

	app: GUI_App_State
	app.controller = controller
	app.selected_field = 0
	app.export_path = "./gui_export.pdf"
	app.project_path = "./gui_project.comic.json"
	app.local_script_pages = "2"
	app.autosave_interval_text = "20"
	app.export_format = .PDF
	app.use_fal_panels = false
	app.status_msg = fmt.aprintf("GUI ready")
	app.autosave_enabled = true
	app.autosave_interval_s = 20
	app.last_autosave_at = rl.GetTime()
	app.last_save_at = -1
	app.last_export_at = -1
	app.log_oldest_first = false
	app.summary_opts = Summary_View_Options{}
	push_status(&app.status_msg, &app.action_log, app.status_msg)
	defer action_log_dispose(&app.action_log)
	defer delete(app.status_msg)
	defer unload_panel_textures(&app.panel_textures)

	for !rl.WindowShouldClose() {
		screen_w := rl.GetScreenWidth()
		screen_h := rl.GetScreenHeight()
		compact_mode := shared.is_compact(screen_h)
		cfg := shared.load_config()
		has_deepseek_key := len(cfg.deepseek_api_key) > 0
		has_fal_key := len(cfg.fal_api_key) > 0

		clay_update_dimensions(screen_w, screen_h)
		clay_update_input()

		// ─── Keyboard Input ──────────────────────────────────────
		interaction_locked := app.show_help_overlay || app.show_confirm_overlay

		if rl.IsKeyPressed(.SLASH) { toggle_help_overlay(&app.show_help_overlay) }
		if rl.IsKeyPressed(.ESCAPE) { close_help_overlay_if_open(&app.show_help_overlay) }

		if !interaction_locked {
			if rl.IsKeyPressed(.ONE) { _ = ui.navigate_to_screen(&app.controller, .Story) }
			if rl.IsKeyPressed(.TWO) { _ = ui.navigate_to_screen(&app.controller, .Script) }
			if rl.IsKeyPressed(.THREE) { _ = ui.navigate_to_screen(&app.controller, .Characters) }
			if rl.IsKeyPressed(.FOUR) { _ = ui.navigate_to_screen(&app.controller, .Panels) }
			if rl.IsKeyPressed(.FIVE) { _ = ui.navigate_to_screen(&app.controller, .Layout) }
			if rl.IsKeyPressed(.SIX) { _ = ui.navigate_to_screen(&app.controller, .Bubbles) }
			if rl.IsKeyPressed(.SEVEN) { _ = ui.navigate_to_screen(&app.controller, .Export) }
			if rl.IsKeyPressed(.EIGHT) { _ = ui.navigate_to_screen(&app.controller, .Community) }
			if rl.IsKeyPressed(.TAB) { app.selected_field = (app.selected_field + 1) % 7 }
			if rl.IsKeyPressed(.F1) { app.selected_field = 0 }
			if rl.IsKeyPressed(.F2) { app.selected_field = 1 }
			if rl.IsKeyPressed(.F3) { app.selected_field = 2 }
			if rl.IsKeyPressed(.F4) { app.selected_field = 3 }
			if rl.IsKeyPressed(.F11) { app.selected_field = 4 }
			if rl.IsKeyPressed(.F12) { app.selected_field = 5 }
		}

		pages_count := parse_pages_or_default(app.local_script_pages, 2)
		autosave_secs := parse_autosave_interval(app.autosave_interval_text, 20)
		app.autosave_interval_s = f64(autosave_secs)
		if app.selected_field != 6 && len(strings.trim_space(app.autosave_interval_text)) == 0 {
			app.autosave_interval_text = fmt.aprintf("%d", autosave_secs)
		}
		shift_down := rl.IsKeyDown(.LEFT_SHIFT) || rl.IsKeyDown(.RIGHT_SHIFT)
		can_generate_panels := len(app.controller.state.script.pages) > 0
		can_layout := len(app.controller.state.panel_images) > 0
		can_export := len(app.controller.state.page_layouts) > 0 && len(app.controller.state.panel_images) > 0
		project_path_ok := project_path_is_normalized(app.project_path)
		export_path_ok := export_path_matches_format(app.export_path, app.export_format)

		// ─── Confirm Overlay Logic ────────────────────────────────
		if app.show_confirm_overlay {
			confirm_yes := rl.IsKeyPressed(.ENTER) || rl.IsKeyPressed(.Y)
			confirm_no := rl.IsKeyPressed(.ESCAPE) || rl.IsKeyPressed(.N)
			if clicked(clay.ID("confirm_yes")) { confirm_yes = true }
			if clicked(clay.ID("confirm_no")) { confirm_no = true }
			if confirm_no {
				app.show_confirm_overlay = false
				app.pending_confirm = .None
				push_status(&app.status_msg, &app.action_log, "Cancelled destructive action")
			} else if confirm_yes {
				action := app.pending_confirm
				app.show_confirm_overlay = false
				app.pending_confirm = .None
				push_status(&app.status_msg, &app.action_log, resolve_confirm_action_with_message(action, &app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at))
			}
		}

		// ─── Text Input ────────────────────────────────────────────
		if !interaction_locked {
			for {
				ch := rl.GetCharPressed()
				if ch == 0 { break }
				if ch < 32 || ch > 126 { continue }
				if app.selected_field == 6 && (ch < '0' || ch > '9') { continue }
				switch app.selected_field {
				case 0: append_char(&app.controller.state.story_idea, ch)
				case 1: append_char(&app.controller.state.story_genre, ch)
				case 2: append_char(&app.controller.state.target_audience, ch)
				case 3: append_char(&app.export_path, ch)
				case 4: append_char(&app.local_script_pages, ch)
				case 5: append_char(&app.project_path, ch)
				case 6: append_char(&app.autosave_interval_text, ch)
				case 7: append_char(&app.bubble_edit_text, ch)
				}
				app.is_dirty = true
			}
			if rl.IsKeyPressed(.BACKSPACE) {
				switch app.selected_field {
				case 0: pop_char(&app.controller.state.story_idea)
				case 1: pop_char(&app.controller.state.story_genre)
				case 2: pop_char(&app.controller.state.target_audience)
				case 3: pop_char(&app.export_path)
				case 4: pop_char(&app.local_script_pages)
				case 5: pop_char(&app.project_path)
				case 6: pop_char(&app.autosave_interval_text)
				case 7: pop_char(&app.bubble_edit_text)
				}
app.is_dirty = true
				}
			}

		// ─── Keyboard Shortcuts ─────────────────────────────────────
		ctrl_down := rl.IsKeyDown(.LEFT_CONTROL) || rl.IsKeyDown(.RIGHT_CONTROL)
		if !interaction_locked {
			if ctrl_down && rl.IsKeyPressed(.S) {
				push_status(&app.status_msg, &app.action_log, save_project_session_with_message(&app.project_path, app.controller.state, &app.is_dirty, &app.last_autosave_at, &app.last_save_at, "Saved project"))
			}
			if ctrl_down && rl.IsKeyPressed(.N) {
				if app.is_dirty && !shift_down {
					push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Reset_Project, "Confirm reset?"))
				} else {
					push_status(&app.status_msg, &app.action_log, reset_project_session(&app.controller, &app.is_dirty, &app.last_autosave_at, true))
				}
			}
			if ctrl_down && rl.IsKeyPressed(.O) {
				if app.is_dirty && !shift_down {
					push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Open_Project, "Confirm open?"))
				} else {
					push_status(&app.status_msg, &app.action_log, open_project_session(&app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at))
				}
			}
			if ctrl_down && rl.IsKeyPressed(.E) {
				push_status(&app.status_msg, &app.action_log, run_export_action(&app.controller, &app.export_path, app.export_format, can_export, &app.is_dirty, &app.last_export_at))
			}
			if ctrl_down && rl.IsKeyPressed(.H) {
				push_status_if_nonempty(&app.status_msg, &app.action_log, toggle_summary_show_if_supported(app.controller.active_screen, &app.summary_opts))
			}
			if ctrl_down && rl.IsKeyPressed(.J) {
				push_status_if_nonempty(&app.status_msg, &app.action_log, toggle_summary_sort_if_supported(app.controller.active_screen, &app.summary_opts))
			}
			if ctrl_down && rl.IsKeyPressed(.MINUS) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, autosave_secs-5))
			}
			if ctrl_down && rl.IsKeyPressed(.EQUAL) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, autosave_secs+5))
			}
			if ctrl_down && rl.IsKeyPressed(.SEVEN) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, 15))
			}
			if ctrl_down && rl.IsKeyPressed(.EIGHT) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, 30))
			}
			if ctrl_down && rl.IsKeyPressed(.NINE) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, 60))
			}
			if ctrl_down && rl.IsKeyPressed(.ZERO) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, reset_helper_fields_with_message(&app.export_path, &app.local_script_pages, &app.autosave_interval_text, app.export_format))
			}
			if ctrl_down && !shift_down && rl.IsKeyPressed(.L) {
				set_status(&app.status_msg, clear_action_log_with_message(&app.action_log))
			}
			if ctrl_down && rl.IsKeyPressed(.V) {
				push_status_if_nonempty(&app.status_msg, &app.action_log, paste_clipboard_into_selected_field_with_message(app.selected_field, &app.controller.state, &app.export_path, &app.local_script_pages, &app.project_path, &app.autosave_interval_text, &app.is_dirty))
			}
			if ctrl_down && rl.IsKeyPressed(.BACKSPACE) {
				push_status(&app.status_msg, &app.action_log, clear_selected_field_with_message(app.selected_field, &app.controller.state, &app.export_path, &app.local_script_pages, &app.project_path, &app.autosave_interval_text, &app.is_dirty))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.C) {
				push_status(&app.status_msg, &app.action_log, copy_text_with_status(app.status_msg, "Copied status to clipboard"))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.A) {
				push_status(&app.status_msg, &app.action_log, toggle_autosave_with_message(&app.autosave_enabled))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.X) {
				push_status(&app.status_msg, &app.action_log, copy_text_with_status(app.export_path, "Copied export path to clipboard"))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.P) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_export_preset_with_message(&app.export_path, app.export_format))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.D) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_export_path_from_project_with_message(&app.export_path, app.project_path, app.export_format))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.G) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_project_path_from_export_with_message(&app.project_path, app.export_path))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.J) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, normalize_project_path_with_message(&app.project_path))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.K) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, normalize_project_path_with_message(&app.project_path))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.M) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, normalize_export_path_with_message(&app.export_path, app.export_format))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.U) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, fix_all_paths_with_message(&app.project_path, &app.export_path, app.export_format))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.F) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, normalize_export_path_with_message(&app.export_path, app.export_format))
			}
			if rl.IsKeyPressed(.F5) {
				push_status(&app.status_msg, &app.action_log, action_generate_deepseek_script(&app.controller, pages_count))
			}
			if rl.IsKeyPressed(.F6) {
				push_status(&app.status_msg, &app.action_log, "Use FAL panel button to generate panels")
			}
			if rl.IsKeyPressed(.F7) {
				push_status(&app.status_msg, &app.action_log, run_layout_action(&app.controller, can_layout, &app.is_dirty))
			}
			if rl.IsKeyPressed(.F8) {
				push_status(&app.status_msg, &app.action_log, run_export_action(&app.controller, &app.export_path, app.export_format, can_export, &app.is_dirty, &app.last_export_at))
			}
			if rl.IsKeyPressed(.F9) {
				push_status(&app.status_msg, &app.action_log, run_next_action(&app.controller, &app.export_path, app.export_format, pages_count, &app.is_dirty, &app.last_export_at))
			}
			if rl.IsKeyPressed(.F10) {
				push_status(&app.status_msg, &app.action_log, run_auto_all_action(&app.controller, &app.export_path, app.export_format, pages_count, &app.is_dirty, &app.last_export_at))
			}
		}

		// ─── Autosave ──────────────────────────────────────────────
		push_status_if_nonempty(&app.status_msg, &app.action_log, autosave_tick_with_message(&app.project_path, app.controller.state, app.autosave_enabled, &app.is_dirty, &app.last_autosave_at, &app.last_save_at, app.autosave_interval_s))

		// ─── Clay Layout Declaration ──────────────────────────────

		clay.BeginLayout()

		// Root: horizontal layout (sidebar + main)
		if clay.UI(clay.ID("Root"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .LeftToRight},
			backgroundColor = CLAY_BG_BASE,
		}) {
			// ─── Sidebar ───────────────────────────────────────
			declare_sidebar(&app)

			// ─── Main Content ────────────────────────────────
			if clay.UI(clay.ID("MainArea"))({
				layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom},
			}) {
				declare_pipeline_bar(&app)
				declare_workspace(&app, can_generate_panels, can_layout, can_export, project_path_ok, export_path_ok, has_deepseek_key, pages_count)
				next_hint := gui_next_hint(app.controller)
				declare_bottom_bar(&app, can_generate_panels, can_layout, can_export, next_hint, has_fal_key)
			}
		}

		// ─── Floating Overlays (Clay) ──────────────────────────────
		if app.show_help_overlay {
			declare_help_overlay()
		}
		if app.show_confirm_overlay {
			declare_confirm_overlay(app.pending_confirm)
		}
		declare_toast(&app.action_log)

		render_commands := clay.EndLayout(rl.GetFrameTime())

		// ─── Click Detection (after layout, before render) ────────
		process_clicks(&app, can_generate_panels, can_layout, can_export, project_path_ok, export_path_ok, has_deepseek_key, has_fal_key, pages_count, shift_down, autosave_secs, compact_mode)

		// ─── Render ────────────────────────────────────────────────────
		rl.BeginDrawing()
		rl.ClearBackground(BG_BASE)
		clay_raylib_render(&render_commands)

		rl.EndDrawing()
	}

	core.dispose_state(state)
	state^ = app.controller.state
	app.controller.state = core.Comic_State{}
	return shared.ok()
}

// ─── Click Handlers (called by process_clicks) ───────────────────

handle_nav_clicks :: proc(app: ^GUI_App_State) {
	nav_screen := []ui.App_Screen{.Story, .Script, .Characters, .Panels, .Layout, .Bubbles, .Export, .Community}
	for i in 0 ..< len(nav_screen) {
		if clicked(clay.ID("Nav", u32(i))) {
			push_status(&app.status_msg, &app.action_log, navigate_screen_with_status(&app.controller, nav_screen[i]))
		}
	}
	pipeline_screens := []ui.App_Screen{.Script, .Panels, .Layout, .Export}
	for i in 0 ..< len(pipeline_screens) {
		if clicked(clay.ID("PStep", u32(i))) {
			push_status(&app.status_msg, &app.action_log, navigate_screen_with_status(&app.controller, pipeline_screens[i]))
		}
	}
}

handle_field_clicks :: proc(app: ^GUI_App_State) {
	input_ids := []string{"field_idea", "field_genre", "field_audience", "field_export", "field_pages", "field_project"}
	for i in 0 ..< len(input_ids) {
		if clicked(clay.ID(input_ids[i])) { app.selected_field = i }
	}
}

handle_format_clicks :: proc(app: ^GUI_App_State, has_deepseek: bool) {
	if clicked(clay.ID("btn_pdf")) { push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .PDF, &app.is_dirty)) }
	if clicked(clay.ID("btn_png")) { push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .PNG, &app.is_dirty)) }
	if clicked(clay.ID("btn_cbz")) { push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .CBZ, &app.is_dirty)) }
}

handle_action_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_export: bool, pages_count: int, shift_down: bool, proj_ok, export_ok: bool, autosave_secs: int, has_fal_key: bool) {
	if clicked(clay.ID("btn_new")) {
		if app.is_dirty && !shift_down { push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Reset_Project, "Confirm reset?")) }
		else { push_status(&app.status_msg, &app.action_log, reset_project_session(&app.controller, &app.is_dirty, &app.last_autosave_at, false)) }
	}
	if clicked(clay.ID("btn_script")) { push_status(&app.status_msg, &app.action_log, action_generate_deepseek_script(&app.controller, pages_count)) }
	if clicked(clay.ID("btn_panels")) { push_status(&app.status_msg, &app.action_log, "Click [FAL] then [Panels] to generate via FAL") }
	if clicked(clay.ID("btn_layout")) { push_status(&app.status_msg, &app.action_log, run_layout_action(&app.controller, can_layout, &app.is_dirty)) }
	if clicked(clay.ID("btn_export")) { push_status(&app.status_msg, &app.action_log, run_export_action(&app.controller, &app.export_path, app.export_format, can_export, &app.is_dirty, &app.last_export_at)) }
	if clicked(clay.ID("btn_export_now")) { push_status(&app.status_msg, &app.action_log, run_export_action(&app.controller, &app.export_path, app.export_format, can_export, &app.is_dirty, &app.last_export_at)) }
	if clicked(clay.ID("btn_export_png")) {
		push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .PNG, &app.is_dirty))
		push_status(&app.status_msg, &app.action_log, run_export_action(&app.controller, &app.export_path, app.export_format, can_export, &app.is_dirty, &app.last_export_at))
	}
	if clicked(clay.ID("btn_export_cbz")) {
		push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .CBZ, &app.is_dirty))
		push_status(&app.status_msg, &app.action_log, run_export_action(&app.controller, &app.export_path, app.export_format, can_export, &app.is_dirty, &app.last_export_at))
	}
	if clicked(clay.ID("btn_next")) { push_status(&app.status_msg, &app.action_log, run_next_action(&app.controller, &app.export_path, app.export_format, pages_count, &app.is_dirty, &app.last_export_at)) }
	if clicked(clay.ID("btn_auto")) { push_status(&app.status_msg, &app.action_log, run_auto_all_action(&app.controller, &app.export_path, app.export_format, pages_count, &app.is_dirty, &app.last_export_at)) }
	if clicked(clay.ID("btn_save")) { push_status(&app.status_msg, &app.action_log, save_project_session_with_message(&app.project_path, app.controller.state, &app.is_dirty, &app.last_autosave_at, &app.last_save_at, "Saved project")) }
	if clicked(clay.ID("btn_open")) {
		if app.is_dirty && !shift_down { push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Open_Project, "Confirm open?")) }
		else { push_status(&app.status_msg, &app.action_log, open_project_session(&app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at)) }
	}
	if clicked(clay.ID("btn_autosave_toggle")) { push_status(&app.status_msg, &app.action_log, toggle_autosave_with_message(&app.autosave_enabled)) }
	if clicked(clay.ID("btn_help")) { toggle_help_overlay(&app.show_help_overlay) }
	if clicked(clay.ID("btn_fal_panels")) {
		if !has_fal_key { push_status(&app.status_msg, &app.action_log, "FAL key missing (set FAL_API_KEY)") }
		else { app.use_fal_panels = !app.use_fal_panels; push_status(&app.status_msg, &app.action_log, fmt.tprintf("FAL panels: %s", "ON" if app.use_fal_panels else "OFF")) }
	}

	diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first)
	if clicked(clay.ID("btn_log_reset")) { push_status(&app.status_msg, &app.action_log, reset_log_view_with_message(&app.log_show_lines, &app.log_oldest_first)) }
	if clicked(clay.ID("btn_log_clear")) { set_status(&app.status_msg, clear_action_log_with_message(&app.action_log)) }
	if clicked(clay.ID("btn_log_report")) { push_status(&app.status_msg, &app.action_log, write_session_report_with_message(diag_ctx)) }
	if clicked(clay.ID("btn_log_copy")) { push_status(&app.status_msg, &app.action_log, copy_action_log_snapshot_with_message(diag_ctx)) }
	if clicked(clay.ID("btn_log_diag")) { push_status(&app.status_msg, &app.action_log, write_diagnostics_with_message(diag_ctx)) }
	if clicked(clay.ID("btn_log_status_copy")) { push_status(&app.status_msg, &app.action_log, copy_text_with_status(app.status_msg, "Copied status to clipboard")) }
	if clicked(clay.ID("btn_log_diag_copy")) { push_status(&app.status_msg, &app.action_log, copy_diagnostics_with_message(diag_ctx)) }
}

handle_workspace_nav :: proc(app: ^GUI_App_State) {
	screen := app.controller.active_screen
	if screen == .Script {
		page_count := len(app.controller.state.script.pages)
		if clicked(clay.ID("btn_script_prev")) && page_count > 0 {
			app.summary_opts.script_page_cursor -= 1
			if app.summary_opts.script_page_cursor < 0 { app.summary_opts.script_page_cursor = page_count - 1 }
		}
		if clicked(clay.ID("btn_script_next")) && page_count > 0 {
			app.summary_opts.script_page_cursor += 1
			if app.summary_opts.script_page_cursor >= page_count { app.summary_opts.script_page_cursor = 0 }
		}
	}
	if screen == .Panels {
		panel_count := count_script_panels(app.controller.state.script)
		if clicked(clay.ID("btn_panels_prev")) && panel_count > 0 {
			app.summary_opts.panel_cursor -= 1
			if app.summary_opts.panel_cursor < 0 { app.summary_opts.panel_cursor = panel_count - 1 }
		}
		if clicked(clay.ID("btn_panels_next")) && panel_count > 0 {
			app.summary_opts.panel_cursor += 1
			if app.summary_opts.panel_cursor >= panel_count { app.summary_opts.panel_cursor = 0 }
		}
	}
	if screen == .Layout {
		layout_count := len(app.controller.state.page_layouts)
		if clicked(clay.ID("btn_layout_prev")) && layout_count > 0 {
			app.summary_opts.layout_page_cursor -= 1
			if app.summary_opts.layout_page_cursor < 0 { app.summary_opts.layout_page_cursor = layout_count - 1 }
		}
		if clicked(clay.ID("btn_layout_next")) && layout_count > 0 {
			app.summary_opts.layout_page_cursor += 1
			if app.summary_opts.layout_page_cursor >= layout_count { app.summary_opts.layout_page_cursor = 0 }
		}
	}
	if screen == .Bubbles {
		layout_count := len(app.controller.state.page_layouts)
		if clicked(clay.ID("btn_bubbles_prev_page")) && layout_count > 0 {
			app.summary_opts.bubble_page_cursor -= 1
			if app.summary_opts.bubble_page_cursor < 0 { app.summary_opts.bubble_page_cursor = layout_count - 1 }
			app.summary_opts.bubble_panel_cursor = 0
			app.summary_opts.bubble_edit_cursor = 0
		}
		if clicked(clay.ID("btn_bubbles_next_page")) && layout_count > 0 {
			app.summary_opts.bubble_page_cursor += 1
			if app.summary_opts.bubble_page_cursor >= layout_count { app.summary_opts.bubble_page_cursor = 0 }
			app.summary_opts.bubble_panel_cursor = 0
			app.summary_opts.bubble_edit_cursor = 0
		}
		if layout_count > 0 {
			page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
			layout_val := app.controller.state.page_layouts[page_idx]
			panel_count := len(layout_val.panels)
			if clicked(clay.ID("btn_bubbles_prev_panel")) && panel_count > 0 {
				app.summary_opts.bubble_panel_cursor -= 1
				if app.summary_opts.bubble_panel_cursor < 0 { app.summary_opts.bubble_panel_cursor = panel_count - 1 }
				app.summary_opts.bubble_edit_cursor = 0
			}
			if clicked(clay.ID("btn_bubbles_next_panel")) && panel_count > 0 {
				app.summary_opts.bubble_panel_cursor += 1
				if app.summary_opts.bubble_panel_cursor >= panel_count { app.summary_opts.bubble_panel_cursor = 0 }
				app.summary_opts.bubble_edit_cursor = 0
			}
		}
	}
}

handle_detail_clicks :: proc(app: ^GUI_App_State) {
	screen := app.controller.active_screen

	if screen == .Panels {
		if clicked(clay.ID("btn_panel_regenerate")) {
			panel_count := count_script_panels(app.controller.state.script)
			if panel_count > 0 {
				idx := clamp_panel_cursor(panel_count, app.summary_opts.panel_cursor)
				panel, _, ok := panel_by_flat_index(app.controller.state.script, idx)
				if ok { push_status(&app.status_msg, &app.action_log, action_regenerate_panel(&app.controller, panel.panel_id)) }
			}
		}
		panel_count := count_script_panels(app.controller.state.script)
		if panel_count > 0 {
			for i in 0..<panel_count {
				if clicked(clay.ID(fmt.tprintf("panel_row_%d", i))) {
					app.summary_opts.panel_cursor = i
					push_status(&app.status_msg, &app.action_log, fmt.tprintf("Viewing panel %d/%d", i+1, panel_count))
				}
			}
		}
	}

	if screen == .Layout {
		if clicked(clay.ID("btn_layout_regen")) {
			push_status(&app.status_msg, &app.action_log, action_regenerate_page_layout(&app.controller, app.summary_opts.layout_page_cursor))
			app.is_dirty = true
		}
		layout_count := len(app.controller.state.page_layouts)
		for i in 0..<layout_count {
			if clicked(clay.ID(fmt.tprintf("layout_row_%d", i))) {
				app.summary_opts.layout_page_cursor = i
				push_status(&app.status_msg, &app.action_log, fmt.tprintf("Viewing layout page %d/%d", i+1, layout_count))
			}
		}
	}

	if screen == .Bubbles {
		layout_count := len(app.controller.state.page_layouts)
		if layout_count > 0 {
			if clicked(clay.ID("btn_bubble_auto_place")) {
				page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
				layout_val := app.controller.state.page_layouts[page_idx]
				if len(layout_val.panels) > 0 {
					panel_idx := clamp_bubble_cursor(len(layout_val.panels), app.summary_opts.bubble_panel_cursor)
					panel_id := layout_val.panels[panel_idx].panel_id
					push_status(&app.status_msg, &app.action_log, action_auto_place_bubbles_for_panel(&app.controller, panel_id, layout_val))
					app.is_dirty = true
				}
			}
			if clicked(clay.ID("btn_bubble_add")) {
				page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
				layout_val := app.controller.state.page_layouts[page_idx]
				if len(layout_val.panels) > 0 {
					panel_idx := clamp_bubble_cursor(len(layout_val.panels), app.summary_opts.bubble_panel_cursor)
					panel_id := layout_val.panels[panel_idx].panel_id
					push_status(&app.status_msg, &app.action_log, action_add_bubble(&app.controller, panel_id))
					app.is_dirty = true
				}
			}
			if clicked(clay.ID("field_bubble_text")) {
				page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
				layout_val := app.controller.state.page_layouts[page_idx]
				if len(layout_val.panels) > 0 {
					panel_idx := clamp_bubble_cursor(len(layout_val.panels), app.summary_opts.bubble_panel_cursor)
					panel_id := layout_val.panels[panel_idx].panel_id
					bubble_count := count_bubbles_for_panel(app.controller.state.speech_bubbles, panel_id)
					if bubble_count > 0 && app.summary_opts.bubble_edit_cursor < bubble_count {
						bubbles := app.controller.state.speech_bubbles[panel_id]
						app.bubble_edit_text = bubbles[app.summary_opts.bubble_edit_cursor].text
						app.selected_field = 7
					}
				}
			}
			if clicked(clay.ID("btn_bubble_save_text")) {
				page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
				layout_val := app.controller.state.page_layouts[page_idx]
				if len(layout_val.panels) > 0 && len(app.bubble_edit_text) > 0 {
					panel_idx := clamp_bubble_cursor(len(layout_val.panels), app.summary_opts.bubble_panel_cursor)
					panel_id := layout_val.panels[panel_idx].panel_id
					if bubbles, ok := app.controller.state.speech_bubbles[panel_id]; ok {
						if app.summary_opts.bubble_edit_cursor < len(bubbles) {
							current_type := bubbles[app.summary_opts.bubble_edit_cursor].type
							push_status(&app.status_msg, &app.action_log, action_update_bubble(&app.controller, panel_id, app.summary_opts.bubble_edit_cursor, current_type, app.bubble_edit_text))
							app.is_dirty = true
						}
					}
				}
				app.selected_field = 0
			}
		}
		if layout_count > 0 {
			page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
			layout_val := app.controller.state.page_layouts[page_idx]
			if len(layout_val.panels) > 0 {
				panel_idx := clamp_bubble_cursor(len(layout_val.panels), app.summary_opts.bubble_panel_cursor)
				panel_id := layout_val.panels[panel_idx].panel_id
				panel_panel_count := count_bubbles_for_panel(app.controller.state.speech_bubbles, panel_id)
				for i in 0..<panel_panel_count {
					if clicked(clay.ID(fmt.tprintf("bubble_row_%d", i))) {
						app.summary_opts.bubble_edit_cursor = i
					}
					if clicked(clay.ID(fmt.tprintf("btn_bubble_delete_%d", i))) {
						push_status(&app.status_msg, &app.action_log, action_delete_bubble(&app.controller, panel_id, i))
						app.is_dirty = true
						if app.summary_opts.bubble_edit_cursor > 0 { app.summary_opts.bubble_edit_cursor -= 1 }
					}
				}
				bubble_count := count_bubbles_for_panel(app.controller.state.speech_bubbles, panel_id)
				if bubble_count > 0 && app.summary_opts.bubble_edit_cursor < bubble_count {
					types := []core.Bubble_Type{.Normal, .Thought, .Shout, .Whisper, .Narration, .Sound_Effect}
					for t in types {
						btn_id := fmt.tprintf("btn_btype_%d", int(t))
						if clicked(clay.ID(btn_id)) {
							push_status(&app.status_msg, &app.action_log, action_update_bubble(&app.controller, panel_id, app.summary_opts.bubble_edit_cursor, t, ""))
							app.is_dirty = true
						}
					}
				}
			}
		}
	}
}
process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_export, proj_ok, export_ok, has_deepseek, has_fal_key: bool, pages_count: int, shift_down: bool, autosave_secs: int, compact_mode: bool) {
	handle_nav_clicks(app)
	handle_field_clicks(app)
	handle_format_clicks(app, has_deepseek)
	handle_action_clicks(app, can_gen_panels, can_layout, can_export, pages_count, shift_down, proj_ok, export_ok, autosave_secs, has_fal_key)
	handle_workspace_nav(app)
	handle_detail_clicks(app)

	// Overlay clicks
	if app.show_confirm_overlay {
		confirm_yes := rl.IsKeyPressed(.ENTER) || rl.IsKeyPressed(.Y) || clicked(clay.ID("confirm_yes"))
		confirm_no := rl.IsKeyPressed(.ESCAPE) || rl.IsKeyPressed(.N) || clicked(clay.ID("confirm_no"))
		if confirm_no {
			app.show_confirm_overlay = false
			app.pending_confirm = .None
			push_status(&app.status_msg, &app.action_log, "Cancelled destructive action")
		} else if confirm_yes {
			app.show_confirm_overlay = false
			msg := resolve_confirm_action_with_message(app.pending_confirm, &app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at)
			push_status(&app.status_msg, &app.action_log, msg)
			app.pending_confirm = .None
		}
	}
	if app.show_help_overlay {
		if rl.IsKeyPressed(.ESCAPE) || rl.IsKeyPressed(.SLASH) {
			app.show_help_overlay = false
			push_status(&app.status_msg, &app.action_log, "Closed help overlay")
		}
	}
}
// ─── Help Overlay (Clay floating) ──────────────────────────────────
declare_help_overlay :: proc() {
	// Backdrop
	if clay.UI(clay.ID("HelpBackdrop"))({
		layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}},
		backgroundColor = {0, 0, 0, 180},
	}) {}

	// Card
	if clay.UI(clay.ID("HelpCard"))({
		layout = {sizing = {width = clay.SizingFixed(860), height = clay.SizingFixed(642)}, layoutDirection = .TopToBottom, padding = {top = 28, right = 30, bottom = 28, left = 30}, childGap = 16},
		backgroundColor = CLAY_BG_CARD,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_LG),
		border = {color = CLAY_BORDER_CARD, width = clay.BorderOutside(2)},
		floating = {offset = {0, 0}, attachTo = .Root, attachment = {element = .CenterCenter, parent = .CenterCenter}, zIndex = 10},
	}) {
		clay_title_text("Keyboard Shortcuts", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_2XL)

		clay_body_text("Navigation", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("1..8 screens | TAB fields | click to focus | F11 pages | F12 project")

		clay_body_text("Core Actions", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("F5 script  F6 panels  F7 layout  F8 export  F9 next  F10 auto-all")
		clay_muted_text("Ctrl+S save | Ctrl+O open | Ctrl+N new | Ctrl+E export | Ctrl+H/J summary")

		clay_body_text("Clipboard + Logs", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("Ctrl+L clear log | Ctrl+Shift+L copy log | Ctrl+Shift+T/B log view | Ctrl+Shift+Z reset")
		clay_muted_text("Ctrl+Shift+C status | Ctrl+Shift+Y diag copy | Ctrl+Shift+R diag file | Ctrl+Shift+W report")
		clay_muted_text("Ctrl+0 reset helpers | Ctrl+V paste | Ctrl+Shift+I copy | Ctrl+Backspace clear")

		clay_body_text("Paths", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("Ctrl+Shift+X copy export | P preset | D exp-from-project | G proj-from-export")
		clay_muted_text("Ctrl+Shift+J fix project | F fix export | K/M quick-fix P/E | U fix all")

		clay_body_text("Autosave", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("Ctrl+Shift+A toggle | Ctrl+-/= adjust | Ctrl+7/8/9 set 15/30/60")

		clay_body_text("Safety", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("Dirty guard: Shift-click New/Open | Keyboard confirm: Ctrl+Shift+N / Ctrl+Shift+O")

		clay_body_text("Close help: Esc or /", color = CLAY_TEXT_TERTIARY, size = CLAY_FONT_SIZE_MD)
	}
}

// ─── Confirm Overlay (Clay floating) ────────────────────────────────
declare_confirm_overlay :: proc(action: Pending_Confirm_Action) {
	// Backdrop
	if clay.UI(clay.ID("ConfirmBackdrop"))({
		layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}},
		backgroundColor = {0, 0, 0, 180},
	}) {}

	// Card
	if clay.UI(clay.ID("ConfirmCard"))({
		layout = {sizing = {width = clay.SizingFixed(520), height = clay.SizingFixed(230)}, layoutDirection = .TopToBottom, padding = {top = 34, right = 30, bottom = 24, left = 30}, childGap = 12},
		backgroundColor = CLAY_BG_CARD,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_LG),
		border = {color = CLAY_BORDER_CARD, width = clay.BorderOutside(2)},
		floating = {offset = {0, 0}, attachTo = .Root, attachment = {element = .CenterCenter, parent = .CenterCenter}, zIndex = 20},
	}) {
		// Accent bar at top
		if clay.UI(clay.ID("ConfirmAccentBar"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(8)}},
			backgroundColor = CLAY_ACCENT,
			cornerRadius = clay.CornerRadiusAll(4),
		}) {}

		clay_title_text("Confirm destructive action", color = CLAY_ERROR, size = CLAY_FONT_SIZE_XL)
		action_label := "reset"
		if action == .Open_Project { action_label = "open a different project" }
		clay_body_text(fmt.tprintf("You have unsaved changes. Do you want to %s?", action_label), color = CLAY_TEXT_SECONDARY)

		if clay.UI(clay.ID("ConfirmBtnRow"))({layout = clay_row_layout()}) {
			declare_button_danger("confirm_yes", "Confirm")
			declare_button_soft("confirm_no", "Cancel")
		}
		clay_muted_text("Enter/Y confirm  |  Esc/N cancel")
	}
}

// ─── Toast (Clay floating) ──────────────────────────────────────────
declare_toast :: proc(log: ^Action_Log) {
	if log.count == 0 { return }
	age := rl.GetTime() - log.last_push_at
	if age > 2.8 { return }

	idx := (log.count - 1) % len(log.entries)
	if idx < 0 { idx += len(log.entries) }
	msg := log.entries[idx]
	bg := CLAY_ERROR
	if is_warning_message(msg) { bg = CLAY_WARNING }
	else if !is_error_message(msg) { bg = CLAY_SUCCESS }

	if clay.UI(clay.ID("Toast"))({
		layout = {sizing = {width = clay.SizingFit({min = 300, max = 0}), height = clay.SizingFixed(34)}, padding = {top = 6, right = 12, bottom = 6, left = 12}, childAlignment = {y = .Center}},
		backgroundColor = bg,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_MD),
		border = {color = clay.Color{255, 255, 255, 40}, width = clay.BorderOutside(1)},
		floating = {offset = {f32(shared.LAYOUT.sidebar_width + 8), 70}, attachTo = .Root, attachment = {element = .LeftTop, parent = .LeftTop}, zIndex = 30},
	}) {
		clay_body_text(msg, color = CLAY_TEXT_BRIGHT, size = CLAY_FONT_SIZE_SM)
	}
}

