use crate::{
	assets::AssetPath,
	components::{
		Component, ComponentBase, ComponentTrait, RefreshData,
		button::{self, ComponentButton},
	},
	event::CallbackDataCommon,
	i18n::Translation,
	layout::WidgetPair,
	widget::{ConstructEssentials, div::WidgetDiv},
};
use std::{
	cell::RefCell,
	rc::{Rc, Weak},
	sync::Arc,
};
use taffy::{
	AlignItems,
	prelude::{auto, length, percent},
};

pub struct Entry<'a> {
	pub sprite_src: Option<AssetPath<'a>>,
	pub text: Translation,
	pub name: &'a str,
}

pub struct Params<'a> {
	pub style: taffy::Style,
	pub entries: Vec<Entry<'a>>,
	pub selected_entry_name: &'a str, // default: ""
	pub on_select: Option<TabSelectCallback>,
}

struct MountedEntry {
	name: Rc<str>,
	button: Rc<ComponentButton>,
}

pub struct TabSelectEvent {
	pub name: Rc<str>,
}

pub type TabSelectCallback = Rc<dyn Fn(&mut CallbackDataCommon, TabSelectEvent) -> anyhow::Result<()>>;

struct State {
	mounted_entries: Vec<MountedEntry>,
	selected_entry_name: Rc<str>,
	on_select: Option<TabSelectCallback>,
}

struct Data {}

pub struct ComponentTabs {
	base: ComponentBase,
	data: Rc<Data>,
	state: Rc<RefCell<State>>,
}

impl ComponentTrait for ComponentTabs {
	fn base(&self) -> &ComponentBase {
		&self.base
	}

	fn base_mut(&mut self) -> &mut ComponentBase {
		&mut self.base
	}

	fn refresh(&self, _data: &mut RefreshData) {
		// nothing to do
	}
}

impl State {
	fn select_entry(&mut self, common: &mut CallbackDataCommon, name: &Rc<str>) {
		let (color_accent, color_button) = {
			let def = common.state.globals.defaults();
			(def.accent_color, def.button_color)
		};

		for entry in &self.mounted_entries {
			if *entry.name == **name {
				entry.button.set_color(common, color_accent);
			} else {
				entry.button.set_color(common, color_button);
			}
		}
		self.selected_entry_name = name.clone();

		if let Some(on_select) = self.on_select.clone() {
			let evt = TabSelectEvent { name: name.clone() };
			common.alterables.dispatch(Box::new(move |common| {
				(*on_select)(common, evt)?;
				Ok(())
			}));
		}
	}
}

impl ComponentTabs {
	pub fn on_select(&self, callback: TabSelectCallback) {
		self.state.borrow_mut().on_select = Some(callback);
	}
}

pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentTabs>)> {
	let mut style = params.style;

	// force-override style
	style.overflow.y = taffy::Overflow::Scroll;
	style.flex_direction = taffy::FlexDirection::Column;
	style.flex_wrap = taffy::FlexWrap::NoWrap;
	style.align_items = Some(AlignItems::Center);
	style.gap = length(4.0);

	let (root, _) = ess.layout.add_child(ess.parent, WidgetDiv::create(), style)?;

	let mut mounted_entries = Vec::<MountedEntry>::new();

	// Mount entries
	for entry in params.entries {
		let (_, button) = button::construct(
			&mut ConstructEssentials {
				layout: ess.layout,
				parent: root.id,
			},
			button::Params {
				text: Some(entry.text),
				sprite_src: entry.sprite_src,
				style: taffy::Style {
					min_size: taffy::Size {
						width: percent(1.0),
						height: length(32.0),
					},
					justify_content: Some(taffy::JustifyContent::Start),
					..Default::default()
				},
				..Default::default()
			},
		)?;

		mounted_entries.push(MountedEntry {
			name: Rc::from(entry.name),
			button,
		});
	}

	let data = Rc::new(Data {});
	let state = Rc::new(RefCell::new(State {
		selected_entry_name: Rc::from(params.selected_entry_name),
		mounted_entries,
		on_select: params.on_select,
	}));

	// handle button clicks
	for entry in &state.borrow().mounted_entries {
		entry.button.on_click({
			let entry_name = entry.name.clone();
			let state = state.clone();
			Rc::new(move |common, _| {
				state.borrow_mut().select_entry(common, &entry_name);
				Ok(())
			})
		});
	}

	let base = ComponentBase {
		id: root.id,
		lhandles: Default::default(),
	};

	let tabs = Rc::new(ComponentTabs { base, data, state });

	ess.layout.defer_component_refresh(Component(tabs.clone()));
	Ok((root, tabs))
}
