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

use super::*;

// ── leap years ────────────────────────────────────────────────────────────

#[ test ]
fn leap_year_rules()
{
	assert!( is_leap_year( 2024 ) );
	assert!( is_leap_year( 2000 ) );  // div by 400
	assert!( !is_leap_year( 1900 ) ); // div by 100 but not 400
	assert!( !is_leap_year( 2023 ) );
	assert!( is_leap_year( -4 ) );    // proleptic
}

#[ test ]
fn days_in_month_handles_leap_february()
{
	assert_eq!( days_in_month( 2024, 2 ), 29 );
	assert_eq!( days_in_month( 2023, 2 ), 28 );
	assert_eq!( days_in_month( 2024, 4 ), 30 );
	assert_eq!( days_in_month( 2024, 7 ), 31 );
}

#[ test ]
fn days_in_month_zero_for_invalid_month()
{
	assert_eq!( days_in_month( 2024, 0 ),  0 );
	assert_eq!( days_in_month( 2024, 13 ), 0 );
}

// ── day of week ───────────────────────────────────────────────────────────

#[ test ]
fn day_of_week_known_dates()
{
	// 1970-01-01 was a Thursday → 4.
	assert_eq!( day_of_week( 1970, 1, 1 ), 4 );
	// 2000-01-01 was a Saturday → 6.
	assert_eq!( day_of_week( 2000, 1, 1 ), 6 );
	// 2024-02-29 was a Thursday → 4 (leap day).
	assert_eq!( day_of_week( 2024, 2, 29 ), 4 );
	// 2026-05-02 (today, per user clock) was a Saturday → 6.
	assert_eq!( day_of_week( 2026, 5, 2 ), 6 );
}

// ── month arithmetic ──────────────────────────────────────────────────────

#[ test ]
fn add_months_within_year()
{
	assert_eq!( add_months( 2024, 3, 1 ),  ( 2024, 4 ) );
	assert_eq!( add_months( 2024, 3, -1 ), ( 2024, 2 ) );
}

#[ test ]
fn add_months_wraps_into_next_year()
{
	assert_eq!( add_months( 2024, 12, 1 ), ( 2025, 1 ) );
}

#[ test ]
fn add_months_wraps_into_previous_year()
{
	assert_eq!( add_months( 2024, 1, -1 ), ( 2023, 12 ) );
}

#[ test ]
fn add_months_handles_large_deltas()
{
	assert_eq!( add_months( 2024, 1, 25 ),  ( 2026, 2 ) );
	assert_eq!( add_months( 2024, 1, -25 ), ( 2021, 12 ) );
}

// ── Date validity ─────────────────────────────────────────────────────────

#[ test ]
fn date_is_valid_on_realistic_inputs()
{
	assert!( Date::new( 2024, 2, 29 ).is_valid() );
	assert!( !Date::new( 2023, 2, 29 ).is_valid() );  // not leap
	assert!( !Date::new( 2024, 4, 31 ).is_valid() );  // april has 30
	assert!( !Date::new( 2024, 0, 1 ).is_valid() );
	assert!( !Date::new( 2024, 13, 1 ).is_valid() );
	assert!( !Date::new( 2024, 1, 0 ).is_valid() );
}

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

#[ derive( Clone, Debug, PartialEq, Eq ) ]
enum Msg
{
	Pick( Date ),
	Nav( i32, u8 ),
}

#[ test ]
fn defaults_view_to_value_month()
{
	let d: DatePicker<Msg> = date_picker( Date::new( 2024, 7, 15 ) );
	assert_eq!( d.view_year,  2024 );
	assert_eq!( d.view_month, 7 );
}

#[ test ]
fn view_builder_overrides()
{
	let d: DatePicker<Msg> = date_picker( Date::new( 2024, 7, 15 ) ).view( 2025, 1 );
	assert_eq!( d.view_year,  2025 );
	assert_eq!( d.view_month, 1 );
}

#[ test ]
fn on_change_callback_invokes_with_date()
{
	let d: DatePicker<Msg> = date_picker( Date::new( 2024, 7, 15 ) )
		.on_change( Msg::Pick );
	let cb = d.on_change.as_ref().expect( "set" );
	assert_eq!( cb( Date::new( 2024, 7, 16 ) ), Msg::Pick( Date::new( 2024, 7, 16 ) ) );
}

#[ test ]
fn on_navigate_callback_invokes_with_year_month()
{
	let d: DatePicker<Msg> = date_picker( Date::new( 2024, 7, 15 ) )
		.on_navigate( Msg::Nav );
	let cb = d.on_navigate.as_ref().expect( "set" );
	assert_eq!( cb( 2025, 1 ), Msg::Nav( 2025, 1 ) );
}

#[ test ]
fn build_does_not_panic_on_minimal_config()
{
	let _: Element<Msg> = date_picker::<Msg>( Date::new( 2024, 7, 15 ) ).build();
}

#[ test ]
fn build_does_not_panic_when_view_month_is_clamped()
{
	// `view_month = 0` is invalid — build clamps to 1 instead of
	// indexing month_names at -1.
	let mut d: DatePicker<Msg> = date_picker( Date::new( 2024, 7, 15 ) );
	d.view_month = 0;
	let _: Element<Msg> = d.build();
}
