use ltk::core::{ Canvas, RenderOptions, UiSurface };
use ltk::{ button, column, window_controls, Color, Element, Point, WindowButtonKind };

#[ derive( Clone, Debug, PartialEq, Eq ) ]
enum Msg
{
	Pressed,
	Close,
	Maximize,
	Minimize,
}

fn view() -> Element<Msg>
{
	column::<Msg>()
		.padding( 16.0 )
		.push( button( "Press" ).on_press( Msg::Pressed ) )
		.into()
}

fn decoration_view() -> Element<Msg>
{
	window_controls(
		Some( Msg::Minimize ),
		WindowButtonKind::Maximize,
		Some( Msg::Maximize ),
		Some( Msg::Close ),
	)
	.into()
}

#[ test ]
fn core_surface_new_is_software_by_default()
{
	let surface = UiSurface::<Msg>::new( 240, 120 );
	assert!( matches!( surface.canvas(), Canvas::Software( _ ) ) );
	assert!( surface.canvas().borrowed_gles_texture().is_none() );
}

#[ test ]
fn core_surface_renders_and_exposes_handlers_without_run()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let out = surface.render(
		&view(),
		RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) ),
	);

	assert!( out.full_redraw );
	assert_eq!( surface.widget_rects().len(), 1 );

	let first = &surface.widget_rects()[ 0 ];
	let hit   = surface.hit_test( Point { x: first.rect.x + 1.0, y: first.rect.y + 1.0 } );
	assert_eq!( hit, Some( first.flat_idx ) );

	let msg = surface.handlers( first.flat_idx ).and_then( |h| h.press_msg() );
	assert_eq!( msg, Some( Msg::Pressed ) );
}

#[ test ]
fn core_surface_can_drive_window_controls()
{
	let mut surface = UiSurface::<Msg>::new( 180, 48 );
	let _ = surface.render(
		&decoration_view(),
		RenderOptions::full_canvas( 180, 48 ).background( Color::rgb( 0.08, 0.08, 0.09 ) ),
	);

	assert_eq!( surface.widget_rects().len(), 3 );

	let close = surface.widget_rects()[ 2 ].flat_idx;
	let msg   = surface.handlers( close ).and_then( |h| h.press_msg() );
	assert_eq!( msg, Some( Msg::Close ) );
}

#[ test ]
fn core_surface_reports_partial_damage_for_focus_change()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let opts = RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) );
	let _ = surface.render( &view(), opts );

	let idx = surface.widget_rects()[ 0 ].flat_idx;
	surface.set_focused( Some( idx ) );
	let out = surface.render( &view(), opts );

	assert!( !out.full_redraw, "focus-only transition should be partial damage" );
	assert!( !out.damage_rects.is_empty() );
}

// ── interaction-only damage paths ─────────────────────────────────────────────

#[ test ]
fn hover_only_change_produces_partial_damage()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let opts = RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) );
	let _ = surface.render( &view(), opts );

	let idx = surface.widget_rects()[ 0 ].flat_idx;
	surface.set_hovered( Some( idx ) );
	let out = surface.render( &view(), opts );

	assert!( !out.full_redraw, "hover-only transition should be partial damage" );
	assert!( !out.damage_rects.is_empty() );
}

#[ test ]
fn pressed_only_change_produces_partial_damage()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let opts = RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) );
	let _ = surface.render( &view(), opts );

	let idx = surface.widget_rects()[ 0 ].flat_idx;
	surface.set_pressed( Some( idx ) );
	let out = surface.render( &view(), opts );

	assert!( !out.full_redraw );
}

#[ test ]
fn focused_hovered_pressed_setters_round_trip()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let opts = RenderOptions::full_canvas( 240, 120 );
	let _ = surface.render( &view(), opts );

	let idx = surface.widget_rects()[ 0 ].flat_idx;
	surface.set_focused( Some( idx ) );
	surface.set_hovered( Some( idx ) );
	surface.set_pressed( Some( idx ) );

	assert_eq!( surface.focused(), Some( idx ) );
	assert_eq!( surface.hovered(), Some( idx ) );
	assert_eq!( surface.pressed(), Some( idx ) );

	surface.set_focused( None );
	surface.set_hovered( None );
	surface.set_pressed( None );

	assert_eq!( surface.focused(), None );
	assert_eq!( surface.hovered(), None );
	assert_eq!( surface.pressed(), None );
}

// ── full-redraw paths ─────────────────────────────────────────────────────────

#[ test ]
fn first_render_is_a_full_redraw()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let out = surface.render(
		&view(),
		RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) ),
	);
	assert!( out.full_redraw, "the first render of a fresh surface is always a full redraw" );
}

#[ test ]
fn mark_content_dirty_forces_next_render_to_full_redraw()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let opts = RenderOptions::full_canvas( 240, 120 ).background( Color::rgb( 0.1, 0.1, 0.1 ) );
	let _ = surface.render( &view(), opts );

	// No interaction change — the next render would normally be partial.
	surface.mark_content_dirty();
	let out = surface.render( &view(), opts );
	assert!( out.full_redraw );
}

#[ test ]
fn resize_forces_next_render_to_full_redraw()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let _ = surface.render( &view(), RenderOptions::full_canvas( 240, 120 ) );

	surface.resize( 320, 200 );
	assert_eq!( surface.size(), ( 320, 200 ) );

	let out = surface.render( &view(), RenderOptions::full_canvas( 320, 200 ) );
	assert!( out.full_redraw );
}

#[ test ]
fn tree_size_change_forces_full_redraw()
{
	let mut surface = UiSurface::<Msg>::new( 320, 200 );
	let opts = RenderOptions::full_canvas( 320, 200 );
	let _ = surface.render( &view(), opts );
	let initial_count = surface.widget_rects().len();

	// Render a different tree (decoration_view has 3 buttons; view has 1).
	let out = surface.render( &decoration_view(), opts );
	assert!( out.full_redraw );
	assert_ne!( surface.widget_rects().len(), initial_count );
}

// ── lookups ───────────────────────────────────────────────────────────────────

#[ test ]
fn hit_test_outside_any_widget_returns_none()
{
	let mut surface = UiSurface::<Msg>::new( 320, 200 );
	let _ = surface.render( &view(), RenderOptions::full_canvas( 320, 200 ) );

	// (0, 0) is the top-left corner — far from the centred button.
	assert_eq!( surface.hit_test( Point { x: 0.0, y: 0.0 } ), None );
}

#[ test ]
fn handlers_lookup_for_unknown_idx_returns_none()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let _ = surface.render( &view(), RenderOptions::full_canvas( 240, 120 ) );
	assert!( surface.handlers( 999_999 ).is_none() );
}

#[ test ]
fn widget_lookup_for_known_idx_returns_some()
{
	let mut surface = UiSurface::<Msg>::new( 240, 120 );
	let _ = surface.render( &view(), RenderOptions::full_canvas( 240, 120 ) );
	let idx = surface.widget_rects()[ 0 ].flat_idx;
	assert!( surface.widget( idx ).is_some() );
}
