use std::cmp::{Ordering, Reverse};

use simd_csv::ByteRecord;

use crate::collections::TopKHeapMap;
use crate::moonblade::types::DynamicNumber;

#[derive(Debug, Clone)]
pub struct Extent<T: Copy + PartialOrd> {
    extent: Option<(T, T)>,
}

impl<T: Copy + PartialOrd> Extent<T> {
    pub fn new() -> Self {
        Self { extent: None }
    }

    pub fn clear(&mut self) {
        self.extent = None;
    }

    pub fn add(&mut self, value: T) {
        match &mut self.extent {
            None => self.extent = Some((value, value)),
            Some((min, max)) => {
                if value < *min {
                    *min = value;
                } else if value > *max {
                    *max = value;
                }
            }
        }
    }

    pub fn min(&self) -> Option<T> {
        self.extent.map(|e| e.0)
    }

    pub fn max(&self) -> Option<T> {
        self.extent.map(|e| e.1)
    }

    pub fn merge(&mut self, other: Self) {
        match self.extent.as_mut() {
            None => {
                self.extent = other.extent;
            }
            Some((min, max)) => {
                if let Some((other_min, other_max)) = other.extent {
                    if other_min < *min {
                        *min = other_min;
                    }
                    if other_max > *max {
                        *max = other_max;
                    }
                }
            }
        }
    }
}

pub type NumericExtent = Extent<DynamicNumber>;

type ArgExtentEntry = (DynamicNumber, (usize, ByteRecord, Option<usize>));

#[derive(Debug, Clone)]
pub struct ArgExtent {
    extent: Option<(ArgExtentEntry, ArgExtentEntry)>,
}

impl ArgExtent {
    pub fn new() -> Self {
        Self { extent: None }
    }

    pub fn clear(&mut self) {
        self.extent = None;
    }

    pub fn add(
        &mut self,
        index: usize,
        value: DynamicNumber,
        record: &ByteRecord,
        col_index: Option<usize>,
    ) {
        match &mut self.extent {
            None => {
                self.extent = Some((
                    (value, (index, record.clone(), col_index)),
                    (value, (index, record.clone(), col_index)),
                ))
            }
            Some(((min, min_arg), (max, max_arg))) => {
                match value.partial_cmp(min).unwrap() {
                    Ordering::Equal => {
                        if min_arg.0 > index {
                            *min_arg = (index, record.clone(), col_index);
                        }
                    }
                    Ordering::Less => {
                        *min = value;
                        *min_arg = (index, record.clone(), col_index);
                    }
                    Ordering::Greater => match value.partial_cmp(max).unwrap() {
                        Ordering::Equal => {
                            if min_arg.0 > index {
                                *min_arg = (index, record.clone(), col_index);
                            }
                        }
                        Ordering::Greater => {
                            *max = value;
                            *max_arg = (index, record.clone(), col_index);
                        }
                        _ => (),
                    },
                };
            }
        }
    }

    pub fn min(&self) -> Option<DynamicNumber> {
        self.extent.as_ref().map(|e| e.0.0)
    }

    pub fn max(&self) -> Option<DynamicNumber> {
        self.extent.as_ref().map(|e| e.1.0)
    }

    pub fn argmin(&self) -> Option<&(usize, ByteRecord, Option<usize>)> {
        self.extent.as_ref().map(|e| &e.0.1)
    }

    pub fn argmax(&self) -> Option<&(usize, ByteRecord, Option<usize>)> {
        self.extent.as_ref().map(|e| &e.1.1)
    }

    pub fn merge(&mut self, other: Self) {
        if let Some(((min, arg_min), (max, arg_max))) = other.extent {
            self.add(arg_min.0, min, &arg_min.1, arg_min.2);
            self.add(arg_max.0, max, &arg_max.1, arg_max.2);
        }
    }
}

#[derive(Debug, Clone)]
pub struct ArgTop {
    heap: TopKHeapMap<(DynamicNumber, Reverse<usize>), (ByteRecord, Option<usize>)>,
}

impl ArgTop {
    pub fn new(k: usize) -> Self {
        Self {
            heap: TopKHeapMap::with_capacity(k),
        }
    }

    pub fn capacity(&self) -> usize {
        self.heap.capacity()
    }

    pub fn clear(&mut self) {
        self.heap.clear();
    }

    pub fn add(
        &mut self,
        index: usize,
        value: DynamicNumber,
        record: &ByteRecord,
        col_index: Option<usize>,
    ) {
        self.heap
            .push_with((value, Reverse(index)), || (record.clone(), col_index));
    }

    pub fn top_indices(&self) -> impl Iterator<Item = usize> {
        self.heap
            .to_sorted_vec()
            .into_iter()
            .map(|((_, Reverse(i)), _)| i)
    }

    pub fn top_records(&self) -> impl Iterator<Item = (usize, ByteRecord, Option<usize>)> {
        self.heap
            .to_sorted_vec()
            .into_iter()
            .map(|((_, Reverse(i)), r)| (i, r.0, r.1))
    }

    pub fn top_values(&self) -> impl Iterator<Item = DynamicNumber> {
        self.heap.to_sorted_vec().into_iter().map(|((v, _), _)| v)
    }

    pub fn merge(&mut self, other: Self) {
        for (k, v) in other.heap.into_unordered_iter() {
            self.heap.push_with(k, || v);
        }
    }
}

#[derive(Debug, Clone)]
pub struct LexicographicExtent {
    extent: Option<(String, String)>,
}

impl LexicographicExtent {
    pub fn new() -> Self {
        Self { extent: None }
    }

    pub fn clear(&mut self) {
        self.extent = None;
    }

    pub fn add(&mut self, value: &str) {
        match &mut self.extent {
            None => self.extent = Some((value.to_string(), value.to_string())),
            Some((min, max)) => {
                if value < min.as_str() {
                    min.replace_range(.., value);
                } else if value > max.as_str() {
                    max.replace_range(.., value);
                }
            }
        }
    }

    pub fn first(&self) -> Option<String> {
        self.extent.as_ref().map(|e| e.0.clone())
    }

    pub fn last(&self) -> Option<String> {
        self.extent.as_ref().map(|e| e.1.clone())
    }

    pub fn merge(&mut self, other: Self) {
        match self.extent.as_mut() {
            None => {
                self.extent = other.extent;
            }
            Some((min, max)) => {
                if let Some((other_min, other_max)) = other.extent {
                    if other_min < *min {
                        *min = other_min;
                    }
                    if other_max > *max {
                        *max = other_max;
                    }
                }
            }
        }
    }
}
