extends SceneTree

const WorldWeatherServiceType = preload(
	"res://world/world_weather_service.gd"
)
const NetworkWorldWeatherServiceType = preload(
	"res://network/network_world_weather_service.gd"
)
const WorldTimeServiceType = preload("res://world/world_time_service.gd")
const WorldTimeVisualControllerType = preload(
	"res://world/world_time_visual_controller.gd"
)
const FishingContextType = preload("res://fishing/fishing_context.gd")
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
const FishAvailabilityType = preload("res://fish/fish_availability.gd")
const FishableWaterRegionType = preload(
	"res://world/fishable_water_region.gd"
)


func _initialize() -> void:
	call_deferred("_run")


func _run() -> void:
	_validate_weather_scheduler()
	_validate_weather_persistence()
	_validate_snapshot_bounds()
	_validate_fishing_weather_seams()
	_validate_fishing_spot_context()
	_validate_weather_presentation()
	print("World weather validation: PASS")
	quit()


func _validate_weather_scheduler() -> void:
	var weather := WorldWeatherServiceType.new()
	root.add_child(weather)
	weather.begin_authoritative_session(20260802)
	assert(weather.get_weather() == WorldWeatherServiceType.Weather.SUNNY)
	assert(_duration_is_valid(weather))
	weather.advance_weather(weather.get_seconds_remaining() + 0.01)
	assert(weather.get_weather() == WorldWeatherServiceType.Weather.CLOUDY)
	assert(_duration_is_valid(weather))
	weather.advance_weather(weather.get_seconds_remaining() + 0.01)
	assert(weather.get_weather() in [
		WorldWeatherServiceType.Weather.SUNNY,
		WorldWeatherServiceType.Weather.RAINY,
		WorldWeatherServiceType.Weather.FOGGY,
	])
	assert(_duration_is_valid(weather))
	weather.apply_authoritative_snapshot(
		WorldWeatherServiceType.Weather.RAINY, 120.0
	)
	assert(weather.is_raining())
	assert(not weather.is_foggy())
	weather.apply_authoritative_snapshot(
		WorldWeatherServiceType.Weather.FOGGY, 120.0
	)
	assert(weather.is_foggy())
	assert(not weather.is_raining())
	weather.queue_free()


func _duration_is_valid(weather: WorldWeatherServiceType) -> bool:
	var seconds: float = weather.get_seconds_remaining()
	match weather.get_weather():
		WorldWeatherServiceType.Weather.SUNNY:
			return seconds >= 480.0 and seconds <= 900.0
		WorldWeatherServiceType.Weather.CLOUDY:
			return seconds >= 300.0 and seconds <= 720.0
		WorldWeatherServiceType.Weather.RAINY, WorldWeatherServiceType.Weather.FOGGY:
			return seconds >= 300.0 and seconds <= 600.0
	return false


func _validate_weather_persistence() -> void:
	var weather := WorldWeatherServiceType.new()
	root.add_child(weather)
	weather.set_persistence_tracking_enabled(true)
	weather.begin_authoritative_session(20260802)
	weather.apply_authoritative_snapshot(
		WorldWeatherServiceType.Weather.RAINY,
		200.0,
	)
	weather.advance_weather(12.5)
	assert(weather.has_persistent_state())
	assert(
		weather.get_persistent_weather()
		== WorldWeatherServiceType.Weather.RAINY
	)
	assert(is_equal_approx(
		weather.get_persistent_seconds_remaining(),
		187.5,
	))
	weather.set_persistence_tracking_enabled(false)
	weather.begin_remote_session()
	weather.apply_authoritative_snapshot(
		WorldWeatherServiceType.Weather.FOGGY,
		45.0,
	)
	assert(weather.is_foggy())
	assert(
		weather.get_persistent_weather()
		== WorldWeatherServiceType.Weather.RAINY
	)
	assert(is_equal_approx(
		weather.get_persistent_seconds_remaining(),
		187.5,
	))
	weather.set_persistence_tracking_enabled(true)
	weather.begin_authoritative_session(20260803)
	assert(weather.is_raining())
	assert(is_equal_approx(weather.get_seconds_remaining(), 187.5))
	assert(not weather.restore_persistent_state(
		WorldWeatherServiceType.Weather.RAINY,
		WorldWeatherServiceType.MAX_PERSISTED_SECONDS + 1.0,
	))
	weather.queue_free()


func _validate_snapshot_bounds() -> void:
	assert(NetworkWorldWeatherServiceType.validate_snapshot({
		"session_id": "session",
		"weather": int(WorldWeatherServiceType.Weather.FOGGY),
		"seconds_remaining": 300.0,
		"sequence": 2,
	}))
	assert(not NetworkWorldWeatherServiceType.validate_snapshot({
		"session_id": "session",
		"weather": 99,
		"seconds_remaining": 300.0,
		"sequence": 2,
	}))
	assert(not NetworkWorldWeatherServiceType.validate_snapshot({
		"session_id": "session",
		"weather": int(WorldWeatherServiceType.Weather.RAINY),
		"seconds_remaining": 1801.0,
		"sequence": 2,
	}))


func _validate_fishing_weather_seams() -> void:
	var clear_context := FishingContextType.new()
	var rain_context := FishingContextType.new()
	rain_context.is_raining = true
	var fog_context := FishingContextType.new()
	fog_context.is_foggy = true
	fog_context.is_night = true
	var rain_only := FishAvailabilityType.new()
	rain_only.require_rain = true
	assert(not rain_only.is_available(clear_context))
	assert(rain_only.is_available(rain_context))
	var fog_only := FishAvailabilityType.new()
	fog_only.allow_day = false
	fog_only.require_fog = true
	assert(not fog_only.is_available(clear_context))
	assert(fog_only.is_available(fog_context))
	var no_fog := FishAvailabilityType.new()
	no_fog.forbid_fog = true
	assert(no_fog.is_available(clear_context))
	assert(not no_fog.is_available(fog_context))


func _validate_fishing_spot_context() -> void:
	var weather := WorldWeatherServiceType.new()
	weather.begin_remote_session()
	weather.apply_authoritative_snapshot(
		WorldWeatherServiceType.Weather.FOGGY, 200.0
	)
	var fishing_spot := FishingSpotType.new()
	fishing_spot.set("_world_weather", weather)
	var region := FishableWaterRegionType.new()
	region.location_tags = [&"starter_pond"]
	var context: FishingContext = fishing_spot.build_network_context(region)
	assert(context.is_foggy)
	assert(not context.is_raining)
	weather.apply_authoritative_snapshot(
		WorldWeatherServiceType.Weather.RAINY, 200.0
	)
	context = fishing_spot.build_network_context(region)
	assert(context.is_raining)
	assert(not context.is_foggy)
	region.free()
	fishing_spot.free()
	weather.free()


func _validate_weather_presentation() -> void:
	var world_root := Node3D.new()
	root.add_child(world_root)
	var world_environment := WorldEnvironment.new()
	var environment := Environment.new()
	var sky := Sky.new()
	var sky_material := ShaderMaterial.new()
	sky_material.shader = preload(
		"res://world/environment/netfishing_sky.gdshader"
	)
	sky.sky_material = sky_material
	environment.sky = sky
	environment.adjustment_enabled = true
	environment.fog_enabled = true
	world_environment.environment = environment
	world_root.add_child(world_environment)
	var sun := DirectionalLight3D.new()
	world_root.add_child(sun)
	var clock := WorldTimeServiceType.new()
	world_root.add_child(clock)
	clock.begin_session(14.0)
	var weather := WorldWeatherServiceType.new()
	world_root.add_child(weather)
	weather.begin_remote_session()
	var rain_target := Node3D.new()
	world_root.add_child(rain_target)
	var visuals := WorldTimeVisualControllerType.new()
	world_root.add_child(visuals)
	visuals.setup(
		clock, world_environment, sun, weather, rain_target
	)
	var runtime_environment: Environment = world_environment.environment
	var clear_saturation: float = runtime_environment.adjustment_saturation
	visuals.apply_weather_immediately(
		WorldWeatherServiceType.Weather.FOGGY
	)
	assert(runtime_environment.fog_depth_begin <= 4.01)
	assert(runtime_environment.fog_depth_end <= 42.01)
	assert(is_zero_approx(runtime_environment.fog_sky_affect))
	assert(runtime_environment.fog_aerial_perspective >= 0.91)
	assert(is_equal_approx(
		runtime_environment.adjustment_saturation,
		clear_saturation,
	))
	visuals.apply_weather_immediately(
		WorldWeatherServiceType.Weather.RAINY
	)
	var rain := visuals.get_node("LocalRain") as GPUParticles3D
	assert(rain != null)
	assert(rain.emitting)
	assert(rain.amount_ratio > 0.99)
	assert(rain.amount == WorldTimeVisualControllerType.RAIN_PARTICLE_AMOUNT)
	var rain_material := rain.process_material as ParticleProcessMaterial
	assert(rain_material != null)
	assert(is_equal_approx(
		rain_material.initial_velocity_min,
		WorldTimeVisualControllerType.RAIN_VELOCITY_MIN,
	))
	assert(is_equal_approx(
		rain_material.initial_velocity_max,
		WorldTimeVisualControllerType.RAIN_VELOCITY_MAX,
	))
	var rain_mesh := rain.draw_pass_1 as BoxMesh
	assert(rain_mesh != null)
	assert(rain_mesh.size.is_equal_approx(
		WorldTimeVisualControllerType.RAIN_DROP_SIZE
	))
	visuals.apply_weather_immediately(
		WorldWeatherServiceType.Weather.SUNNY
	)
	assert(not rain.emitting)
	world_root.queue_free()
