use crate::dictionary;
use std::{cell::OnceCell, cmp::Ordering, range::Range, sync::LazyLock};

#[derive(Debug)]
pub(crate) struct ErrorCandidate<'text> {
	pub(crate) word: &'text str,
	locations: Vec<Range<usize>>,
	suggestions: OnceCell<Box<[String]>>,
}

impl<'text> ErrorCandidate<'text> {
	pub fn new(
		word: &'text str,
		locations: Vec<Range<usize>>,
	) -> Self {
		Self { word, locations, suggestions: OnceCell::default() }
	}

	pub fn get_suggestions(
		&self,
		dict: &dictionary::Dictionary,
	) -> &[String] {
		self.suggestions.get_or_init(|| dict.suggest(self.word))
	}

	pub fn get_locations(&self) -> &[Range<usize>] {
		&self.locations
	}

	pub fn count(&self) -> usize {
		self.locations.len()
	}

	pub fn _get_context<const CONTEXT: usize>(
		&self,
		text: &'text str,
	) -> (&'text str, &'text str) {
		// GEMINI written function
		const {
			assert!(CONTEXT != 0, "CONTEXT must not be zero");
		}
		let span = self.locations[0];

		let start = text[..span.start]
			.rmatch_indices('\n')
			.nth(CONTEXT - 1)
			.map_or(0, |(i, _)| i + 1);

		let end = text[span.end..]
			.match_indices('\n')
			.nth(CONTEXT - 1)
			.map_or(text.len(), |(i, _)| span.end + i);

		(&text[start..span.start], &text[span.end..end])
	}

	pub fn get_immediate_context<const MAX_DIST: usize>(
		&self,
		text: &'text str,
	) -> (&'text str, &'text str) {
		// GEMINI written function
		let span = self.locations[0];
		let (pre, suf) = (&text[..span.start], &text[span.end..]);

		let p = pre.floor_char_boundary(pre.len().saturating_sub(MAX_DIST));
		let s = suf.ceil_char_boundary(suf.len().min(MAX_DIST));

		let start = p + pre[p..]
			.rfind('\n')
			.or_else(|| pre[p..].find(' '))
			.map_or(0, |i| i + 1);
		let end =
			span.end + suf[..s].find('\n').or_else(|| suf[..s].rfind(' ')).unwrap_or(s);

		(&text[start..span.start], &text[span.end..end])
	}
}

struct UnicodeDataProvider;
mod private {
	// This is for the impl_data_provider macro. This crate has no unsafe out of this and FFI
	#![allow(unused_unsafe)]
	include!("../segmentation_data/mod.rs");
	impl_data_provider!(super::UnicodeDataProvider);
}

pub(crate) struct UnicodeData {
	pub segmenter: LazyLock<icu_segmenter::WordSegmenter>,
}

pub(crate) static UNICODE_DATA: UnicodeData = UnicodeData {
	segmenter: LazyLock::new(|| {
		icu_segmenter::WordSegmenter::try_new_for_non_complex_scripts_unstable(
			&UnicodeDataProvider,
			icu_segmenter::options::WordBreakOptions::default(),
		)
		.unwrap()
	}),
};

#[derive(Eq, PartialEq)]
pub(crate) struct AlphabeticalKey<'text>(pub &'text str);
impl Ord for AlphabeticalKey<'_> {
	fn cmp(
		&self,
		other: &Self,
	) -> Ordering {
		self.0.to_lowercase().cmp(&other.0.to_lowercase())
	}
}

impl PartialOrd for AlphabeticalKey<'_> {
	fn partial_cmp(
		&self,
		other: &Self,
	) -> Option<Ordering> {
		Some(self.cmp(other))
	}
}
