package tests

import "core:fmt"
import "core:os"
import "core:strings"
import "core:testing"
import app "../src/app"
import "../src/core"
import "../src/shared"
import "../src/ui"

@test
cli_parse_commands :: proc(t: ^testing.T) {
	c1 := app.parse_cli_command(nil)
	testing.expect(t, c1.kind == .Demo, "no args should map to demo")

	a2 := [1]string{"status"}
	c2 := app.parse_cli_command(a2[:])
	testing.expect(t, c2.kind == .Status, "status should parse")

	a3 := [2]string{"save", "x.json"}
	c3 := app.parse_cli_command(a3[:])
	testing.expect(t, c3.kind == .Save, "save should parse")
	testing.expect(t, c3.path == "x.json", "save path should parse")

	a4 := [1]string{"tui"}
	c4 := app.parse_cli_command(a4[:])
	testing.expect(t, c4.kind == .Tui, "tui should parse")

	a5 := [1]string{"gui"}
	c5 := app.parse_cli_command(a5[:])
	testing.expect(t, c5.kind == .Gui, "gui should parse")

	testing.expect(t, app.normalize_tui_command("q") == "quit", "q alias should expand")
	testing.expect(t, app.normalize_tui_command("1") == "goto story", "1 alias should map to story")
	testing.expect(t, app.normalize_tui_command("?") == "doctor", "? alias should map to doctor")
	testing.expect(t, app.normalize_tui_command("r") == "ready", "r alias should map to ready")
	testing.expect(t, app.normalize_tui_command("n") == "next", "n alias should map to next")
	testing.expect(t, app.normalize_tui_command("p") == "plan", "p alias should map to plan")
	testing.expect(t, app.normalize_tui_command("x") == "auto", "x alias should map to auto")

	pages, matched, perr := app.parse_generate_script_pages("generate script 6")
	testing.expect(t, shared.is_ok(perr), "generate script pages parse should succeed")
	testing.expect(t, matched, "generate script pages should match")
	testing.expect(t, pages == 6, "generate script pages should parse value")

	local_pages, lmatched, lerr := app.parse_generate_script_local_pages("generate script local 3")
	testing.expect(t, shared.is_ok(lerr), "generate script local parse should succeed")
	testing.expect(t, lmatched, "generate script local should match")
	testing.expect(t, local_pages == 3, "generate script local should parse value")

	page, pmatch, pperr := app.parse_generate_panels_page("generate panels page 2")
	testing.expect(t, shared.is_ok(pperr), "generate panels page parse should succeed")
	testing.expect(t, pmatch, "generate panels page should match")
	testing.expect(t, page == 2, "generate panels page should parse value")

	lpage, lpmatch, lperr := app.parse_generate_panels_local_page("generate panels local page 2")
	testing.expect(t, shared.is_ok(lperr), "generate panels local page parse should succeed")
	testing.expect(t, lpmatch, "generate panels local page should match")
	testing.expect(t, lpage == 2, "generate panels local page should parse value")

	fmt_kind, export_path, ematch, eerr := app.parse_export_command("export cbz ./out.cbz")
	testing.expect(t, shared.is_ok(eerr), "export parse should succeed")
	testing.expect(t, ematch, "export parse should match")
	testing.expect(t, fmt_kind == .CBZ, "export format should parse")
	testing.expect(t, export_path == "./out.cbz", "export path should parse")

	qfmt, qpath, qpages, qmatch, qerr := app.parse_quick_local_command("quick local pdf ./quick.pdf 3")
	testing.expect(t, shared.is_ok(qerr), "quick local parse should succeed")
	testing.expect(t, qmatch, "quick local parse should match")
	testing.expect(t, qfmt == .PDF, "quick local format should parse")
	testing.expect(t, qpath == "./quick.pdf", "quick local path should parse")
	testing.expect(t, qpages == 3, "quick local pages should parse")

	proj, qafmt, qaout, qapages, qamatch, qaerr := app.parse_quick_local_all_command("quick local all ./p.comic.json cbz ./q.cbz 4")
	testing.expect(t, shared.is_ok(qaerr), "quick local all parse should succeed")
	testing.expect(t, qamatch, "quick local all parse should match")
	testing.expect(t, proj == "./p.comic.json", "quick local all project path should parse")
	testing.expect(t, qafmt == .CBZ, "quick local all format should parse")
	testing.expect(t, qaout == "./q.cbz", "quick local all export path should parse")
	testing.expect(t, qapages == 4, "quick local all pages should parse")

	aafmt, aapath, aamatch, aaerr := app.parse_auto_all_command("auto all pdf ./auto.pdf")
	testing.expect(t, shared.is_ok(aaerr), "auto all parse should succeed")
	testing.expect(t, aamatch, "auto all parse should match")
	testing.expect(t, aafmt == .PDF, "auto all format should parse")
	testing.expect(t, aapath == "./auto.pdf", "auto all path should parse")

	aalfmt, aalpath, aalpages, aalmatch, aalerr := app.parse_auto_all_local_command("auto all local cbz ./auto.cbz 3")
	testing.expect(t, shared.is_ok(aalerr), "auto all local parse should succeed")
	testing.expect(t, aalmatch, "auto all local parse should match")
	testing.expect(t, aalfmt == .CBZ, "auto all local format should parse")
	testing.expect(t, aalpath == "./auto.cbz", "auto all local path should parse")
	testing.expect(t, aalpages == 3, "auto all local pages should parse")
}

@test
cli_save_and_load_roundtrip :: proc(t: ^testing.T) {
	tmp_dir, terr := os.make_directory_temp("", "comic-cli-*", context.temp_allocator)
	if terr != nil {
		testing.expect(t, false, "failed to create temp dir")
		return
	}
	defer os.remove_all(tmp_dir)

	path := fmt.aprintf("%s/project.comic.json", tmp_dir)
	defer delete(path)

	state := core.new_initial_state()
	state.story_idea = "cli story"

	save_out, save_err := app.run_cli_command(app.Parsed_CLI_Command{kind = .Save, path = path}, &state)
	testing.expect(t, shared.is_ok(save_err), "save command should succeed")
	testing.expect(t, strings.contains(save_out, "Saved project"), "save output should mention save")
	delete(save_out)

	state.story_idea = "changed"
	load_out, load_err := app.run_cli_command(app.Parsed_CLI_Command{kind = .Load, path = path}, &state)
	testing.expect(t, shared.is_ok(load_err), "load command should succeed")
	testing.expect(t, strings.contains(load_out, "Loaded project"), "load output should mention load")
	testing.expect(t, state.story_idea == "cli story", "load should restore story")
	delete(load_out)

	core.dispose_state_owned(&state)
}

@test
cli_tui_generate_script_requires_key :: proc(t: ^testing.T) {
	prev := os.get_env("DEEPSEEK_API_KEY", context.temp_allocator)
	defer {
		if len(prev) > 0 {
			_ = os.set_env("DEEPSEEK_API_KEY", prev)
		} else {
			_ = os.unset_env("DEEPSEEK_API_KEY")
		}
	}
	_ = os.unset_env("DEEPSEEK_API_KEY")

	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer ui.dispose_controller(&controller)

	last_job := 0
	_, out, err := app.run_tui_command(&controller, "generate script 6", &last_job)
	testing.expect(t, !shared.is_ok(err), "generate script should fail without configured key")
	testing.expect(t, len(out) == 0, "error path should not return output")
}

@test
cli_tui_generate_panels_page_requires_script :: proc(t: ^testing.T) {
	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer ui.dispose_controller(&controller)

	last_job := 0
	_, out, err := app.run_tui_command(&controller, "generate panels page 2", &last_job)
	testing.expect(t, !shared.is_ok(err), "generate panels page should fail with empty script")
	testing.expect(t, len(out) == 0, "error path should not return output")
}

@test
cli_tui_generate_script_local_succeeds_without_key :: proc(t: ^testing.T) {
	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer ui.dispose_controller(&controller)

	last_job := 0
	_, out, err := app.run_tui_command(&controller, "generate script local 2", &last_job)
	testing.expect(t, shared.is_ok(err), "generate script local should succeed without key")
	testing.expect(t, strings.contains(out, "local script generated"), "local generation should return success message")
	delete(out)
	testing.expect(t, len(controller.state.script.pages) == 2, "local script should create requested pages")
}

@test
cli_tui_layout_and_export_require_data :: proc(t: ^testing.T) {
	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer ui.dispose_controller(&controller)

	last_job := 0
	_, out1, err1 := app.run_tui_command(&controller, "layout auto", &last_job)
	testing.expect(t, !shared.is_ok(err1), "layout auto should fail without script")
	testing.expect(t, len(out1) == 0, "layout error path should not return output")

	_, out2, err2 := app.run_tui_command(&controller, "export pdf ./tmp.pdf", &last_job)
	testing.expect(t, !shared.is_ok(err2), "export should fail without layouts")
	testing.expect(t, len(out2) == 0, "export error path should not return output")
}

@test
cli_tui_local_panels_and_export_pdf :: proc(t: ^testing.T) {
	tmp_dir, terr := os.make_directory_temp("", "comic-cli-local-*", context.temp_allocator)
	if terr != nil {
		testing.expect(t, false, "failed to create temp dir")
		return
	}
	defer os.remove_all(tmp_dir)

	out_pdf := fmt.aprintf("%s/local.pdf", tmp_dir)
	defer delete(out_pdf)

	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer {
		for _, img in controller.state.panel_images {
			delete(img.url)
			delete(img.prompt)
		}
		delete(controller.state.panel_images)
		controller.state.panel_images = nil
		ui.dispose_controller(&controller)
	}

	last_job := 0
	_, out1, err1 := app.run_tui_command(&controller, "generate script local 2", &last_job)
	testing.expect(t, shared.is_ok(err1), "local script should succeed")
	delete(out1)

	_, out2, err2 := app.run_tui_command(&controller, "generate panels local", &last_job)
	testing.expect(t, shared.is_ok(err2), "local panels should succeed")
	delete(out2)

	_, out3, err3 := app.run_tui_command(&controller, "layout auto", &last_job)
	testing.expect(t, shared.is_ok(err3), "layout auto should succeed")
	delete(out3)

	export_cmd := fmt.aprintf("export pdf %s", out_pdf)
	defer delete(export_cmd)
	_, out4, err4 := app.run_tui_command(&controller, export_cmd, &last_job)
	testing.expect(t, shared.is_ok(err4), "export should succeed")
	delete(out4)
	testing.expect(t, os.exists(out_pdf), "exported pdf should exist")
}

@test
cli_tui_quick_local_export_pdf :: proc(t: ^testing.T) {
	tmp_dir, terr := os.make_directory_temp("", "comic-cli-quick-*", context.temp_allocator)
	if terr != nil {
		testing.expect(t, false, "failed to create temp dir")
		return
	}
	defer os.remove_all(tmp_dir)

	out_pdf := fmt.aprintf("%s/quick.pdf", tmp_dir)
	defer delete(out_pdf)

	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer {
		for _, img in controller.state.panel_images {
			delete(img.url)
			delete(img.prompt)
		}
		delete(controller.state.panel_images)
		controller.state.panel_images = nil
		ui.dispose_controller(&controller)
	}

	cmd := fmt.aprintf("quick local pdf %s 3", out_pdf)
	defer delete(cmd)
	last_job := 0
	_, out, err := app.run_tui_command(&controller, cmd, &last_job)
	testing.expect(t, shared.is_ok(err), "quick local should succeed")
	testing.expect(t, strings.contains(out, "quick local exported"), "quick local should return success output")
	delete(out)
	testing.expect(t, len(controller.state.script.pages) == 3, "quick local should build requested page count")
	testing.expect(t, os.exists(out_pdf), "quick-local exported pdf should exist")
}

@test
cli_tui_quick_local_all_saves_and_exports :: proc(t: ^testing.T) {
	tmp_dir, terr := os.make_directory_temp("", "comic-cli-quick-all-*", context.temp_allocator)
	if terr != nil {
		testing.expect(t, false, "failed to create temp dir")
		return
	}
	defer os.remove_all(tmp_dir)

	project_path := fmt.aprintf("%s/project.comic.json", tmp_dir)
	export_path := fmt.aprintf("%s/quick.cbz", tmp_dir)
	defer delete(project_path)
	defer delete(export_path)

	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer {
		for _, img in controller.state.panel_images {
			delete(img.url)
			delete(img.prompt)
		}
		delete(controller.state.panel_images)
		controller.state.panel_images = nil
		ui.dispose_controller(&controller)
	}

	cmd := fmt.aprintf("quick local all %s cbz %s 2", project_path, export_path)
	defer delete(cmd)
	last_job := 0
	_, out, err := app.run_tui_command(&controller, cmd, &last_job)
	testing.expect(t, shared.is_ok(err), "quick local all should succeed")
	testing.expect(t, strings.contains(out, "quick local all saved"), "quick local all should return success output")
	delete(out)
	testing.expect(t, os.exists(project_path), "quick local all should save project")
	testing.expect(t, os.exists(export_path), "quick local all should export artifact")
}

@test
cli_tui_doctor_reports_status :: proc(t: ^testing.T) {
	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer ui.dispose_controller(&controller)

	last_job := 0
	_, out, err := app.run_tui_command(&controller, "doctor", &last_job)
	testing.expect(t, shared.is_ok(err), "doctor should succeed")
	testing.expect(t, strings.contains(out, "Doctor"), "doctor output should include header")
	testing.expect(t, strings.contains(out, "deepseek key:"), "doctor output should include deepseek key status")
	testing.expect(t, strings.contains(out, "curl:"), "doctor output should include curl status")
	delete(out)
}

@test
cli_tui_ready_reports_status :: proc(t: ^testing.T) {
	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer ui.dispose_controller(&controller)

	last_job := 0
	_, out, err := app.run_tui_command(&controller, "ready", &last_job)
	testing.expect(t, shared.is_ok(err), "ready should succeed")
	testing.expect(t, strings.contains(out, "Ready"), "ready output should include header")
	testing.expect(t, strings.contains(out, "script generated:"), "ready output should include script status")
	testing.expect(t, strings.contains(out, "export ready:"), "ready output should include export status")
	delete(out)
}

@test
cli_tui_next_recommends_action :: proc(t: ^testing.T) {
	prev_deep := os.get_env("DEEPSEEK_API_KEY", context.temp_allocator)
	prev_fal := os.get_env("FAL_API_KEY", context.temp_allocator)
	defer {
		if len(prev_deep) > 0 { _ = os.set_env("DEEPSEEK_API_KEY", prev_deep) } else { _ = os.unset_env("DEEPSEEK_API_KEY") }
		if len(prev_fal) > 0 { _ = os.set_env("FAL_API_KEY", prev_fal) } else { _ = os.unset_env("FAL_API_KEY") }
	}
	_ = os.unset_env("DEEPSEEK_API_KEY")
	_ = os.unset_env("FAL_API_KEY")

	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer ui.dispose_controller(&controller)
	last_job := 0

	_, out1, err1 := app.run_tui_command(&controller, "next", &last_job)
	testing.expect(t, shared.is_ok(err1), "next should succeed")
	testing.expect(t, strings.contains(out1, "generate script local"), "next should recommend local script generation")
	delete(out1)

	_, out2, err2 := app.run_tui_command(&controller, "generate script local 1", &last_job)
	testing.expect(t, shared.is_ok(err2), "generate script local should succeed")
	delete(out2)

	_, out3, err3 := app.run_tui_command(&controller, "next", &last_job)
	testing.expect(t, shared.is_ok(err3), "next should succeed after script")
	testing.expect(t, strings.contains(out3, "generate panels local"), "next should recommend local panel generation")
	delete(out3)
}

@test
cli_tui_plan_reports_progress :: proc(t: ^testing.T) {
	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer ui.dispose_controller(&controller)
	last_job := 0

	_, out1, err1 := app.run_tui_command(&controller, "plan", &last_job)
	testing.expect(t, shared.is_ok(err1), "plan should succeed")
	testing.expect(t, strings.contains(out1, "Plan"), "plan output should include header")
	testing.expect(t, strings.contains(out1, "1) Script"), "plan output should include script step")
	delete(out1)

	_, out2, err2 := app.run_tui_command(&controller, "generate script local 1", &last_job)
	testing.expect(t, shared.is_ok(err2), "local script should succeed")
	delete(out2)

	_, out3, err3 := app.run_tui_command(&controller, "plan", &last_job)
	testing.expect(t, shared.is_ok(err3), "plan should succeed after script")
	testing.expect(t, strings.contains(out3, "next:"), "plan output should include next hint")
	delete(out3)
}

@test
cli_tui_auto_runs_next_step :: proc(t: ^testing.T) {
	prev_deep := os.get_env("DEEPSEEK_API_KEY", context.temp_allocator)
	prev_fal := os.get_env("FAL_API_KEY", context.temp_allocator)
	defer {
		if len(prev_deep) > 0 { _ = os.set_env("DEEPSEEK_API_KEY", prev_deep) } else { _ = os.unset_env("DEEPSEEK_API_KEY") }
		if len(prev_fal) > 0 { _ = os.set_env("FAL_API_KEY", prev_fal) } else { _ = os.unset_env("FAL_API_KEY") }
	}
	_ = os.unset_env("DEEPSEEK_API_KEY")
	_ = os.unset_env("FAL_API_KEY")

	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer ui.dispose_controller(&controller)
	last_job := 0

	_, out, err := app.run_tui_command(&controller, "auto", &last_job)
	testing.expect(t, shared.is_ok(err), "auto should succeed")
	testing.expect(t, strings.contains(out, "auto ran:"), "auto output should include executed command")
	delete(out)
	testing.expect(t, len(controller.state.script.pages) > 0, "auto should progress by generating script")
}

@test
cli_tui_auto_all_runs_to_export :: proc(t: ^testing.T) {
	prev_deep := os.get_env("DEEPSEEK_API_KEY", context.temp_allocator)
	prev_fal := os.get_env("FAL_API_KEY", context.temp_allocator)
	defer {
		if len(prev_deep) > 0 { _ = os.set_env("DEEPSEEK_API_KEY", prev_deep) } else { _ = os.unset_env("DEEPSEEK_API_KEY") }
		if len(prev_fal) > 0 { _ = os.set_env("FAL_API_KEY", prev_fal) } else { _ = os.unset_env("FAL_API_KEY") }
	}
	_ = os.unset_env("DEEPSEEK_API_KEY")
	_ = os.unset_env("FAL_API_KEY")

	tmp_dir, terr := os.make_directory_temp("", "comic-cli-auto-all-*", context.temp_allocator)
	if terr != nil {
		testing.expect(t, false, "failed to create temp dir")
		return
	}
	defer os.remove_all(tmp_dir)
	out_pdf := fmt.aprintf("%s/auto_all.pdf", tmp_dir)
	defer delete(out_pdf)

	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer {
		for _, img in controller.state.panel_images {
			delete(img.url)
			delete(img.prompt)
		}
		delete(controller.state.panel_images)
		controller.state.panel_images = nil
		ui.dispose_controller(&controller)
	}
	last_job := 0

	cmd := fmt.aprintf("auto all pdf %s", out_pdf)
	defer delete(cmd)
	_, out, err := app.run_tui_command(&controller, cmd, &last_job)
	testing.expect(t, shared.is_ok(err), "auto all should succeed")
	testing.expect(t, strings.contains(out, "auto all exported"), "auto all output should include success")
	delete(out)
	testing.expect(t, os.exists(out_pdf), "auto all should produce export file")
}

@test
cli_tui_auto_all_local_runs_to_export :: proc(t: ^testing.T) {
	tmp_dir, terr := os.make_directory_temp("", "comic-cli-auto-all-local-*", context.temp_allocator)
	if terr != nil {
		testing.expect(t, false, "failed to create temp dir")
		return
	}
	defer os.remove_all(tmp_dir)
	out_cbz := fmt.aprintf("%s/auto_all_local.cbz", tmp_dir)
	defer delete(out_cbz)

	state := core.new_initial_state()
	controller := ui.new_controller(state)
	defer {
		for _, img in controller.state.panel_images {
			delete(img.url)
			delete(img.prompt)
		}
		delete(controller.state.panel_images)
		controller.state.panel_images = nil
		ui.dispose_controller(&controller)
	}
	last_job := 0

	cmd := fmt.aprintf("auto all local cbz %s 2", out_cbz)
	defer delete(cmd)
	_, out, err := app.run_tui_command(&controller, cmd, &last_job)
	testing.expect(t, shared.is_ok(err), "auto all local should succeed")
	testing.expect(t, strings.contains(out, "auto all local exported"), "auto all local output should include success")
	delete(out)
	testing.expect(t, os.exists(out_cbz), "auto all local should produce export file")
}

@test
cli_tui_open_and_saveas_aliases :: proc(t: ^testing.T) {
	tmp_dir, terr := os.make_directory_temp("", "comic-cli-alias-*", context.temp_allocator)
	if terr != nil {
		testing.expect(t, false, "failed to create temp dir")
		return
	}
	defer os.remove_all(tmp_dir)

	path := fmt.aprintf("%s/alias.comic.json", tmp_dir)
	defer delete(path)

	state := core.new_initial_state()
	controller := ui.new_controller(state)

	controller.state.story_idea = "alias story"
	last_job := 0
	save_cmd := fmt.aprintf("saveas %s", path)
	defer delete(save_cmd)
	_, out1, err1 := app.run_tui_command(&controller, save_cmd, &last_job)
	testing.expect(t, shared.is_ok(err1), "saveas should succeed")
	delete(out1)

	controller.state.story_idea = "changed"
	open_cmd := fmt.aprintf("open %s", path)
	defer delete(open_cmd)
	_, out2, err2 := app.run_tui_command(&controller, open_cmd, &last_job)
	testing.expect(t, shared.is_ok(err2), "open should succeed")
	delete(out2)
	testing.expect(t, controller.state.story_idea == "alias story", "open should restore saved state")

	if shared.is_ok(err2) {
		ui.dispose_controller_owned(&controller)
	} else {
		ui.dispose_controller(&controller)
	}
}
