package tests

import "core:strings"
import "core:testing"
import "../src/adapters"
import "../src/core"
import "../src/shared"

deepseek_calls: int

phase2_deepseek_transport :: proc(cfg: shared.Config, request_json: string) -> (string, int, shared.App_Error) {
	_ = cfg
	_ = request_json
	deepseek_calls += 1
	if deepseek_calls == 1 {
		return "", 429, shared.ok()
	}
	return "{\"choices\":[{\"message\":{\"content\":\"{\\\"title\\\":\\\"Test\\\",\\\"synopsis\\\":\\\"A hero rises\\\",\\\"characters\\\":[{\\\"id\\\":\\\"char_001\\\",\\\"name\\\":\\\"Hero\\\",\\\"role\\\":\\\"protagonist\\\",\\\"description\\\":\\\"Determined\\\",\\\"firstAppearancePanel\\\":\\\"panel_001_001\\\"}],\\\"pages\\\":[{\\\"pageNumber\\\":1,\\\"layoutType\\\":\\\"grid\\\",\\\"panels\\\":[{\\\"panelId\\\":\\\"panel_001_001\\\",\\\"panelNumber\\\":1,\\\"shotType\\\":\\\"medium\\\",\\\"description\\\":\\\"Hero stands\\\",\\\"charactersPresent\\\":[\\\"char_001\\\"],\\\"dialogue\\\":[{\\\"speakerId\\\":\\\"char_001\\\",\\\"text\\\":\\\"Let's go!\\\",\\\"bubbleType\\\":\\\"normal\\\",\\\"emotion\\\":\\\"determined\\\"}],\\\"caption\\\":\\\"\\\",\\\"soundEffects\\\":[],\\\"transitionFromPrevious\\\":\\\"none\\\"}]}]}\"}}]}", 200, shared.ok()
}

fal_calls: int

phase2_fal_transport :: proc(cfg: shared.Config, endpoint, prompt, negative_prompt: string, seed: i64, image_size: string, reference_images: []string, reference_strength: f32) -> (string, int, shared.App_Error) {
	_ = cfg
	_ = endpoint
	_ = prompt
	_ = negative_prompt
	_ = seed
	_ = image_size
	_ = reference_images
	_ = reference_strength
	fal_calls += 1
	if fal_calls == 1 {
		return "", 0, shared.network_error("temporary network issue")
	}
	return "https://example.com/image.png", 200, shared.ok()
}

@test
deepseek_retries_then_succeeds :: proc(t: ^testing.T) {
	deepseek_calls = 0

	cfg := shared.Config{
		deepseek_api_key = "test-key",
		deepseek_base_url = "https://api.deepseek.com",
	}
	client := adapters.new_deepseek_client()
	client.transport = phase2_deepseek_transport
	client.max_retries = 3

	opts := adapters.Generate_Script_Options{
		story_idea = "A hero rises",
		genre = "action",
		art_style = "manga",
		num_pages = 4,
		audience = "general",
	}

	script, err := adapters.generate_comic_script(client, cfg, opts)
	defer core.dispose_script_owned(&script)
	testing.expect(t, shared.is_ok(err), "deepseek request should eventually succeed")
	testing.expect(t, deepseek_calls == 2, "expected one retry before success")
	testing.expect(t, len(script.pages) > 0, "generated script should contain pages")
}

@test
fal_queue_enforces_cap :: proc(t: ^testing.T) {
	q := adapters.new_fal_queue(1)

	first := adapters.try_acquire_slot(&q)
	second := adapters.try_acquire_slot(&q)
	adapters.release_slot(&q)
	third := adapters.try_acquire_slot(&q)

	testing.expect(t, first, "first slot acquisition should succeed")
	testing.expect(t, !second, "second slot acquisition should fail when saturated")
	testing.expect(t, third, "slot acquisition should succeed after release")
}

@test
fal_panel_generation_retries_network_error :: proc(t: ^testing.T) {
	fal_calls = 0

	cfg := shared.Config{fal_api_key = "test-key"}
	q := adapters.new_fal_queue(2)
	client := adapters.new_fal_client(&q)
	client.transport = phase2_fal_transport
	client.max_retries = 3

	panel := core.Panel{panel_id = "panel_1", panel_number = 1, description = "Hero jumps over a gap"}
	img, err := adapters.generate_panel_image(client, cfg, panel, nil, "manga", "proj_1")
	defer delete(img.prompt)

	testing.expect(t, shared.is_ok(err), "fal panel generation should eventually succeed")
	testing.expect(t, fal_calls == 2, "expected one retry for network error")
	testing.expect(t, len(img.url) > 0, "image url should be set")
}

@test
fal_typed_response_parsing :: proc(t: ^testing.T) {
	body := "{\"images\":[{\"url\":\"https://example.com/a.png\",\"width\":1024,\"height\":1024}]}"
	resp, err := adapters.fal_parse_response_body(body)
	defer adapters.dispose_fal_response(&resp)

	testing.expect(t, shared.is_ok(err), "typed parse should succeed")
	testing.expect(t, len(resp.images) == 1, "expected one image")
	testing.expect(t, resp.images[0].url == "https://example.com/a.png", "expected parsed URL")
}

stream_chunks_received: int
stream_final_received: bool

test_stream_callback :: proc(chunk: string, is_complete: bool) -> () {
	stream_chunks_received += 1
	if is_complete {
		stream_final_received = true
	}
}

@test
deepseek_stream_callback_receives_chunks :: proc(t: ^testing.T) {
	stream_chunks_received = 0
	stream_final_received = false

	// Test that the stream callback mechanism works
	// We can't easily mock curl streaming in tests, so test the callback type exists
	callback: adapters.Stream_Callback = test_stream_callback
	testing.expect(t, callback != nil, "stream callback type should be callable")
}

@test
deepseek_stream_requires_api_key :: proc(t: ^testing.T) {
	cfg := shared.Config{deepseek_api_key = ""}
	opts := adapters.Generate_Script_Options{
		story_idea = "Test",
		num_pages = 1,
	}

	_, err := adapters.stream_comic_script_stub(cfg, opts, nil)
	testing.expect(t, !shared.is_ok(err), "should fail without API key")
	testing.expect(t, strings.contains(err.message, "DEEPSEEK_API_KEY"), "error should mention missing key")
}

@test
deepseek_stream_validates_options :: proc(t: ^testing.T) {
	cfg := shared.Config{deepseek_api_key = "test-key"}
	opts := adapters.Generate_Script_Options{
		story_idea = "",
		num_pages = 1,
	}

	_, err := adapters.stream_comic_script_stub(cfg, opts, nil)
	testing.expect(t, !shared.is_ok(err), "should fail with empty story_idea")
}
