// Page chrome shared by every wizard screen: top bar (back affordance
// + dot indicator), bottom primary CTA pill, and a single page wrapper
// that applies the standard padding.

use ltk::
{
	Color, Corners, Element,
	column, container, pressable, row, spacer, text, button, ButtonVariant,
};

use crate::app::Msg;
use crate::assets;
use crate::flow::Screen;
use crate::i18n::tr;
use crate::state::WizardMode;
use crate::ui::theme as t;

// ── Page wrapper ─────────────────────────────────────────────────────────────

/// Wrap each screen's body in a full-surface container so the page
/// background is `#EEEEEE` regardless of the active theme palette.
/// The container has no padding — that lives on the body's root
/// `column` so a `Scroll`/`Spacer` child still gets a flex parent.
pub fn page<E>( body: E ) -> Element<Msg>
where
	E: Into<Element<Msg>>,
{
	container::<Msg>( body.into() )
		.background( Color::hex( 0xEE, 0xEE, 0xEE ) )
		.radius( Corners::ZERO )
		.into()
}

// ── Top bar ──────────────────────────────────────────────────────────────────

/// Top bar: optional back link on the left, optional dot indicator on
/// the right. Either side can be omitted by passing `false`.
pub fn top_bar( screen: Screen, show_back: bool, mode: WizardMode ) -> Element<Msg>
{
	let back: Element<Msg> = if show_back
	{
		back_button()
	} else {
		spacer().into()
	};

	let mut bar = row::<Msg>().spacing( t::TIGHT_SPACING ).push( back );

	if let Some( ( current, total ) ) = screen.dot_indicator( mode )
	{
		bar = bar
			.push( spacer() )
			.push( dots( current, total ) );
	} else if let Some( ( current, total ) ) = screen.tutorial_dots() {
		bar = bar
			.push( spacer() )
			.push( dots( current, total ) );
	}

	bar.into()
}

/// Tiny progress-dot strip used by the wizard's top bar. The active
/// step is rendered as an elongated pill; inactive steps as small
/// circles.
pub fn dots( current: usize, total: usize ) -> Element<Msg>
{
	let palette = ltk::theme_palette();
	let active  = palette.text_primary;
	let muted   = palette.divider;

	let mut r = row::<Msg>().spacing( t::DOT_SPACING );
	for i in 0..total
	{
		let is_active = i == current;
		let w = if is_active { t::DOT_ACTIVE } else { t::DOT_SIZE };
		let h = t::DOT_SIZE;
		let cell: Element<Msg> = container::<Msg>(
			spacer().width( w ).height( h ),
		)
		.background( if is_active { active } else { muted } )
		.radius( Corners::all( h / 2.0 ) )
		.into();
		r = r.push( cell );
	}
	r.into()
}

// ── Title block ──────────────────────────────────────────────────────────────

/// Standard title + optional subtitle pair, centred and matching the
/// figma type scale.
pub fn title_block( title: &str, subtitle: Option<&str> ) -> Element<Msg>
{
	let palette   = ltk::theme_palette();
	let primary   = palette.text_primary;
	let secondary = palette.text_secondary;

	let mut col = column::<Msg>().spacing( t::TIGHT_SPACING );
	col = col.push(
		text( title )
			.size( t::TITLE_SIZE )
			.color( primary )
			.align_center(),
	);
	if let Some( s ) = subtitle
	{
		col = col.push(
			text( s )
				.size( t::SUBTITLE_SIZE )
				.color( secondary )
				.align_center()
				.wrap( true ),
		);
	}
	col.into()
}

// ── CTA pill ─────────────────────────────────────────────────────────────────

/// Wide primary CTA pill anchored at the bottom of the page (the cyan
/// "Continue" / "Get started" button used across the flow).
pub fn primary_cta( label: &str, on_press: Option<Msg> ) -> Element<Msg>
{
	// Keep the pill at full width by wrapping it in a row whose
	// children expand. Disabled state is conveyed by `on_press_maybe`
	// — when `None` the button renders muted.
	row::<Msg>()
		.spacing( 0.0 )
		.push(
			button::<Msg>( label )
				.variant( ButtonVariant::Primary )
				.font_size( t::control_font_size() )
				.on_press_maybe( on_press )
				.height( t::control_height() ),
		)
		.into()
}

/// Tertiary "skip" affordance used under the primary CTA (e.g. "Skip
/// for now" on the security and tutorial screens).
pub fn skip_link( label: &str, on_press: Msg ) -> Element<Msg>
{
	button::<Msg>( label )
		.variant( ButtonVariant::Tertiary )
		.font_size( t::control_font_size() )
		.on_press( on_press )
		.height( t::control_height() )
		.into()
}

fn back_button() -> Element<Msg>
{
	let palette = ltk::theme_palette();
	let icon: Element<Msg> = assets::theme_icon::<Msg>( "general/left", 16 )
		.unwrap_or_else( || spacer().into() );

	let label = text( tr!( "common.back" ).to_string() )
		.size( t::BODY_SIZE )
		.color( palette.text_primary );

	pressable::<Msg>(
		row::<Msg>()
			.spacing( 6.0 )
			.push( icon )
			.push( label ),
	)
	.on_press( Msg::Back )
	.into()
}

/// Convenience: the very common "Back" string that callers don't need
/// to translate themselves.
#[ allow( dead_code ) ]
pub fn back_label() -> String { tr!( "common.back" ).to_string() }

/// Convenience for the standard primary "Continue" label, translated
/// against the active locale.
#[ allow( dead_code ) ]
pub fn continue_label() -> String { tr!( "common.continue" ).to_string() }

#[ allow( dead_code ) ]
pub fn fill_color() -> Color { ltk::theme_palette().accent }

/// Approximate height of everything a hero-illustration page shows
/// besides the illustration itself: window chrome, top bar, title
/// block, CTA, skip link, page paddings and section gaps.
const HERO_RESERVED: u32 = 440;

/// Size for a full-width hero illustration: the page width minus the
/// horizontal padding, additionally bounded by the viewport height
/// minus [`HERO_RESERVED`] (plus `extra_below` for screens with more
/// content under the illustration, e.g. an info card) so the text
/// below still fits without scrolling, and capped at `cap` so desktop
/// viewports don't get billboard-scale artwork.
pub fn hero_illustration_size( viewport: ( u32, u32 ), cap: u32, extra_below: u32 ) -> u32
{
	let ( w, h ) = viewport;
	let by_width  = w.saturating_sub( ( t::PAGE_PADDING_H * 2.0 ) as u32 );
	let by_height = h.saturating_sub( HERO_RESERVED + extra_below );
	by_width.min( by_height ).clamp( 96, cap )
}
