//! Middle row of the QS panel — the media card on the left half,
//! the brightness + volume slider pair on the right half. Width
//! split is 50/50 via `row( flex(media_card), flex(sliders_pair) )`.
//! The card height matches `VSLIDER_H` (220 px) so the row's
//! preferred height is unambiguous regardless of media state.

use std::sync::Arc;

use crate::message::Message;
use crate::mpris::MediaState;
use crate::system::SystemState;

use rust_i18n::t;

use super::super::icons::Icons;
use super::super::{
	CARD_RADIUS,
	QS_VSLIDER_W_VW, QS_VSLIDER_H_VW, QS_CARD_ICON_VW, QS_GRID_GAP_VW,
	QS_MEDIA_TEXT_VW, QS_CARD_ROW_GAP_VW, QS_OUTER_PAD_VW,
	QS_TRANSPORT_ICON_VW, QS_TRANSPORT_PAD_VW, QS_TRANSPORT_MARGIN_VW, QS_CARD_PADDING_H_VW,
};

/// `viewport_w` = logical viewport width (physical screen / scale). The media
/// card does layout arithmetic (cover aspect ratio, between-row spacer) that
/// needs concrete px, so it resolves the width-fraction sizes through this.
pub( super ) fn build(
	icons:        &Icons,
	system_state: &SystemState,
	media_state:  Option<&MediaState>,
	palette:      &ltk::Palette,
	viewport_w:   f32,
) -> ltk::Element<Message>
{
	let vw         = |pct: f32| pct / 100.0 * viewport_w;
	let grid_row_h = ( vw( QS_VSLIDER_H_VW ) - vw( QS_GRID_GAP_VW ) ) / 2.0;
	let card_w     = ( viewport_w - 2.0 * vw( QS_OUTER_PAD_VW ) - vw( QS_GRID_GAP_VW ) ) / 2.0;

	let mc = media_card( icons, media_state, palette, grid_row_h, viewport_w, card_w );
	let sp = sliders_pair( icons, system_state, palette );

	ltk::row::<Message>()
		.padding( 0.0 )
		.spacing( ltk::Length::vw( QS_GRID_GAP_VW ) )
		.fill_height()
		.push( ltk::flex( mc ) )
		.push( ltk::flex( sp ) )
		.into()
}

// ─── media card ─────────────────────────────────────────────────────────────

/// Always rendered: empty MPRIS state shows a `text-secondary`
/// "no hay reproducción" label with the transport icons greyed out.
pub( super ) fn media_card(
	icons:       &Icons,
	media_state: Option<&MediaState>,
	palette:     &ltk::Palette,
	grid_row_h:  f32,
	viewport_w:  f32,
	card_w:      f32,
) -> ltk::Element<Message>
{
	let vw             = |pct: f32| pct / 100.0 * viewport_w;
	let transport_icon = vw( QS_TRANSPORT_ICON_VW );
	let transport_pad  = vw( QS_TRANSPORT_PAD_VW );
	let transport_margin = vw( QS_TRANSPORT_MARGIN_VW );
	let card_pad_h     = vw( QS_CARD_PADDING_H_VW );
	let row_gap        = vw( QS_CARD_ROW_GAP_VW );
	let cover_h        = grid_row_h;

	let ( media_label, label_color, center_icon ) = match media_state
	{
		Some( m ) if !m.title.is_empty() =>
		{
			let icon = if m.playing { &icons.pause } else { &icons.play };
			( m.title.clone(), palette.text_primary, icon )
		}
		_ =>
		(
			t!( "qs_no_playback_history" ).to_string(),
			palette.text_secondary,
			&icons.play,
		),
	};

	// Width follows the source aspect ratio at fixed `cover_h`, so a
	// 16:9 video thumbnail is shown wide instead of squished into a
	// square cell — clamped to the card's inner width (shrinking the
	// height with it) so a wide cover cannot bleed past the padding.
	// Placeholder during the art-download window is a neutral square
	// so the card height does not jump on arrival.
	let ( cover_element, cover_present ) = match media_state
	{
		Some( m ) =>
		{
			let el: ltk::Element<Message> = match ( &m.art_rgba, m.art_dims )
			{
				( Some( rgba ), Some( ( w, h ) ) ) if w > 0 && h > 0 =>
				{
					let aspect    = w as f32 / h as f32;
					let avail_w   = ( card_w - 2.0 * card_pad_h ).max( 1.0 );
					let display_w = ( cover_h * aspect ).min( avail_w );
					let display_h = display_w / aspect;
					ltk::img_widget( Arc::clone( rgba ), w, h )
						.size( display_w, display_h )
						.into()
				}
				_ => ltk::spacer().width( cover_h ).height( cover_h ).into(),
			};
			( el, true )
		}
		None => ( ltk::spacer().into(), false ),
	};

	// Browsers serving YouTube sometimes leave `CanGoPrevious` /
	// `CanGoNext` stuck at `false` after an ad break, producing
	// spuriously-disabled arrows. We trust what the player reports.
	let prev_enabled = media_state.map( | m | m.can_go_previous ).unwrap_or( false );
	let next_enabled = media_state.map( | m | m.can_go_next     ).unwrap_or( false );
	let play_enabled = media_state.is_some();

	let label = ltk::text( media_label )
		.size( ltk::Length::vw( QS_MEDIA_TEXT_VW ) )
		.color( label_color );
	let transport_row = ltk::row::<Message>()
		.padding( 0.0 )
		.spacing( ltk::Length::vw( QS_GRID_GAP_VW ) )
		.push( media_button( icons, &icons.previous, Message::MediaPrevious,  prev_enabled, transport_icon, transport_pad, palette ) )
		.push( media_button( icons, center_icon,     Message::MediaPlayPause, play_enabled, transport_icon, transport_pad, palette ) )
		.push( media_button( icons, &icons.next,     Message::MediaNext,      next_enabled, transport_icon, transport_pad, palette ) );

	// Weighted spacer: the card is stretched by the row's `fill_height`
	// to match its sibling column exactly, so the gap absorbs whatever
	// is left and the transport row pins to the card's bottom edge.
	let mut media_inner = ltk::column::<Message>()
		.padding( 0.0 )
		.spacing( 0.0 )
		.push( label );
	if cover_present
	{
		media_inner = media_inner
			.push( ltk::spacer().height( row_gap ) )
			.push( cover_element );
	}
	let media_inner = media_inner
		.push( ltk::spacer() )
		.push( transport_row );

	ltk::container::<Message>( media_inner )
		.surface( "surface-card-flat" )
		.radius( CARD_RADIUS )
		.padding_h( card_pad_h )
		.padding_v( transport_margin )
		.into()
}

/// Each transport button is enabled / disabled independently based
/// on what the active player advertises through MPRIS:
/// `CanGoPrevious` / `CanGoNext` for the arrows, and "is there any
/// player at all" for the play/pause centre button. A disabled
/// button is tinted with `text_secondary` and has no `on_press` —
/// pressing it is a visual no-op rather than firing a
/// `Message::Media*` that the bus thread would just reject
/// downstream.
fn media_button(
	icons:          &Icons,
	icon_slot:      &Option<( Arc<Vec<u8>>, u32, u32 )>,
	msg:            Message,
	enabled:        bool,
	transport_icon: f32,
	transport_pad:  f32,
	palette:        &ltk::Palette,
) -> ltk::Element<Message>
{
	let tint = if enabled { palette.text_primary } else { palette.text_secondary };
	match icon_slot
	{
		Some( ( raw, w, h ) ) =>
		{
			let tinted = icons.tint_cached( raw, tint );
			let inner = ltk::container::<Message>(
				ltk::img_widget( tinted, *w, *h )
					.size( transport_icon, transport_icon )
			)
			.padding( transport_pad );
			if enabled
			{
				ltk::pressable( inner ).on_press( msg ).into()
			}
			else
			{
				inner.into()
			}
		}
		None => ltk::spacer().into(),
	}
}

// ─── slider pair ────────────────────────────────────────────────────────────

/// Brightness on the left, volume on the right — matches the Figma
/// reference (`assets/figma/quicksettings.png`). Each pill is a
/// `stack` of the vslider and a small bottom-anchored glyph
/// (sun / speaker), with `Center / Center` alignment so the
/// vslider keeps its `VSLIDER_W × VSLIDER_H` preferred size — a
/// `Fill` push would stretch it to the stack's outer rect.
fn sliders_pair( icons: &Icons, system_state: &SystemState, palette: &ltk::Palette ) -> ltk::Element<Message>
{
	let icon_color   = palette.text_primary;
	let bright_value = system_state.brightness as f32 / 100.0;
	let vol_value    = system_state.audio.volume as f32 / 100.0;

	let bright_pill = slider_pill( icons, bright_value, Message::SetBrightness, &icons.brightness_slider, icon_color );
	let vol_pill    = slider_pill( icons, vol_value,    Message::SetVolume,     &icons.speaker_slider,    icon_color );

	ltk::row::<Message>()
		.padding( 0.0 )
		.spacing( ltk::Length::vw( QS_GRID_GAP_VW ) )
		.push( ltk::flex( bright_pill ) )
		.push( ltk::flex( vol_pill ) )
		.into()
}

fn slider_pill(
	icons:      &Icons,
	value:      f32,
	on_change:  fn( f32 ) -> Message,
	icon_slot:  &Option<( Arc<Vec<u8>>, u32, u32 )>,
	icon_color: ltk::Color,
) -> ltk::Element<Message>
{
	ltk::stack::<Message>()
		.push_aligned(
			ltk::vslider( value )
				.on_change( on_change )
				.track_surface( "surface-slider-track-flat" )
				.fill_surface( "surface-slider-fill-flat" )
				.size( ltk::Length::vw( QS_VSLIDER_W_VW ), ltk::Length::vw( QS_VSLIDER_H_VW ) ),
			ltk::HAlign::Center, ltk::VAlign::Center,
		)
		.push_aligned_margin(
			slider_icon( icons, icon_slot, icon_color ),
			ltk::HAlign::Center, ltk::VAlign::Bottom, 18.0,
		)
		.into()
}

fn slider_icon(
	icons:     &Icons,
	icon_slot: &Option<( Arc<Vec<u8>>, u32, u32 )>,
	icon_color: ltk::Color,
) -> ltk::Element<Message>
{
	match icon_slot
	{
		Some( ( raw, w, h ) ) =>
		{
			let tinted = icons.tint_cached( raw, icon_color );
			ltk::img_widget( tinted, *w, *h ).size( ltk::Length::vw( QS_CARD_ICON_VW ), ltk::Length::vw( QS_CARD_ICON_VW ) ).into()
		}
		None => ltk::spacer().into(),
	}
}
