// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

use super::*;

// ── hex serialization ─────────────────────────────────────────────────────

#[ test ]
fn color_to_hex_uppercase_six_digits_for_opaque()
{
	let c = Color::rgba( 1.0, 0.0, 0.5, 1.0 );
	assert_eq!( color_to_hex( c, false ), "#FF0080" );
	assert_eq!( color_to_hex( c, true  ), "#FF0080" );  // alpha=255 stripped
}

#[ test ]
fn color_to_hex_emits_eight_digits_when_alpha_kept_and_present()
{
	let c = Color::rgba( 1.0, 0.0, 0.5, 0.5 );
	// 0.5 × 255 = 127.5 → rounds to 128 = 0x80.
	assert_eq!( color_to_hex( c, true ), "#FF008080" );
}

#[ test ]
fn color_to_hex_clamps_out_of_range()
{
	let c = Color::rgba( 1.5, -0.1, 0.0, 1.0 );
	assert_eq!( color_to_hex( c, false ), "#FF0000" );
}

// ── hex parsing ───────────────────────────────────────────────────────────

#[ test ]
fn parse_hex_six_digits()
{
	let c = parse_hex( "#FF0080" ).expect( "ok" );
	assert!( ( c.r - 1.0 ).abs() < 1e-3 );
	assert_eq!( c.g, 0.0 );
	assert!( ( c.b - 0.502 ).abs() < 1e-2 );
	assert_eq!( c.a, 1.0 );
}

#[ test ]
fn parse_hex_three_digit_shorthand()
{
	let c = parse_hex( "#F08" ).expect( "ok" );
	// "F" → 0xFF. "0" → 0x00. "8" → 0x88.
	assert_eq!( ( c.r * 255.0 ).round() as u8, 0xFF );
	assert_eq!( ( c.g * 255.0 ).round() as u8, 0x00 );
	assert_eq!( ( c.b * 255.0 ).round() as u8, 0x88 );
}

#[ test ]
fn parse_hex_eight_digit_includes_alpha()
{
	let c = parse_hex( "#FF008080" ).expect( "ok" );
	assert!( ( c.a * 255.0 ).round() as u8 == 0x80 );
}

#[ test ]
fn parse_hex_optional_leading_hash()
{
	assert!( parse_hex( "FF0080" ).is_some() );
	assert!( parse_hex( "#FF0080" ).is_some() );
}

#[ test ]
fn parse_hex_case_insensitive()
{
	let upper = parse_hex( "#A1B2C3" ).unwrap();
	let lower = parse_hex( "#a1b2c3" ).unwrap();
	assert_eq!( upper, lower );
}

#[ test ]
fn parse_hex_rejects_garbage()
{
	assert!( parse_hex( "" ).is_none() );
	assert!( parse_hex( "#" ).is_none() );
	assert!( parse_hex( "#XYZ" ).is_none() );
	// 5 / 7 / 9 digits — not one of the valid widths (3/4/6/8).
	assert!( parse_hex( "#12345" ).is_none() );
	assert!( parse_hex( "#1234567" ).is_none() );
	// Non-hex character inside a valid-length string.
	assert!( parse_hex( "#GG0000" ).is_none() );
}

#[ test ]
fn parse_hex_round_trips_through_color_to_hex()
{
	let original = Color::rgba( 0.2, 0.4, 0.6, 1.0 );
	let s        = color_to_hex( original, false );
	let parsed   = parse_hex( &s ).expect( "ok" );
	// Allow ±1/255 quantisation slack.
	assert!( ( parsed.r - original.r ).abs() < 1.0 / 255.0 + 1e-6 );
	assert!( ( parsed.g - original.g ).abs() < 1.0 / 255.0 + 1e-6 );
	assert!( ( parsed.b - original.b ).abs() < 1.0 / 255.0 + 1e-6 );
}

// ── builders ──────────────────────────────────────────────────────────────

#[ derive( Clone, Debug, PartialEq ) ]
enum Msg { Pick( Color ) }

#[ test ]
fn defaults()
{
	let p: ColorPicker<Msg> = color_picker( Color::WHITE );
	assert_eq!( p.value, Color::WHITE );
	assert!( !p.show_alpha );
	assert!( p.on_change.is_none() );
}

#[ test ]
fn build_does_not_panic_minimal()
{
	let _: Element<Msg> = color_picker::<Msg>( Color::WHITE ).build();
}

#[ test ]
fn build_does_not_panic_with_alpha_and_callback()
{
	let _: Element<Msg> = color_picker::<Msg>( Color::rgba( 0.2, 0.4, 0.6, 0.8 ) )
		.show_alpha( true )
		.on_change( Msg::Pick )
		.build();
}

// ── Hue arithmetic ────────────────────────────────────────────────────────

#[ test ]
fn rgb_to_hue_pure_primaries()
{
	assert!( ( rgb_to_hue( 1.0, 0.0, 0.0 ) -   0.0 ).abs() < 1e-3 );
	assert!( ( rgb_to_hue( 1.0, 1.0, 0.0 ) -  60.0 ).abs() < 1e-3 );
	assert!( ( rgb_to_hue( 0.0, 1.0, 0.0 ) - 120.0 ).abs() < 1e-3 );
	assert!( ( rgb_to_hue( 0.0, 1.0, 1.0 ) - 180.0 ).abs() < 1e-3 );
	assert!( ( rgb_to_hue( 0.0, 0.0, 1.0 ) - 240.0 ).abs() < 1e-3 );
	assert!( ( rgb_to_hue( 1.0, 0.0, 1.0 ) - 300.0 ).abs() < 1e-3 );
}

#[ test ]
fn rgb_to_hue_grey_returns_zero()
{
	assert_eq!( rgb_to_hue( 0.5, 0.5, 0.5 ), 0.0 );
	assert_eq!( rgb_to_hue( 0.0, 0.0, 0.0 ), 0.0 );
}

#[ test ]
fn hue_to_rgb_round_trips_at_primaries()
{
	for deg in [ 0.0, 60.0, 120.0, 180.0, 240.0, 300.0 ]
	{
		let ( r, g, b ) = hue_to_rgb( deg );
		let h           = rgb_to_hue( r, g, b );
		let diff = ( ( h - deg + 540.0 ) % 360.0 - 180.0 ).abs();
		assert!( diff < 1e-3, "hue {deg} degrees did not round-trip (got {h})" );
	}
}
