use std::cmp::Ordering;

use anyhow::anyhow;

use crate::field::Field;
use crate::key::Key;
use crate::order::Order;

#[derive(Debug)]
pub(crate) struct LineRecord {
    line: String,
    keys: Vec<Key>,
    order: Order,
}

impl LineRecord {
    pub(crate) fn new(line: String, fields: &Vec<Field>, field_separator: char, order: Order) -> Result<LineRecord, anyhow::Error> {
        if fields.len() == 1 && fields[0].index() == 0 {
            let field = &fields[0];
            let key = Key::new(line.as_str(), field)
                .map_err(|e| anyhow!("line: {line}, error: {e}"))?;
            Ok(
                LineRecord {
                    line,
                    keys: vec![key],
                    order,
                }
            )
        } else {
            let mut keys: Vec<Key> = Vec::new();
            let parts: Vec<&str> = line.split(field_separator).collect();
            let mut error = None;
            for field in fields {
                if field.index() == 0 {
                    error = Some(
                        anyhow!(
                            "Field index of 0 must be specified only once, meaning the entire line is to be used as a key".to_string()
                        )
                    );
                    break;
                }
                if field.index() > parts.len() {
                    error = Some(
                        anyhow!(
                            "Requested comparison for field {} but there are only {} fields using {} as field separator.",
                            field.index(),
                            parts.len(),
                            field_separator,
                       )
                    );
                    break;
                }
                keys.push(Key::new(parts[field.index() - 1], field)?)
            }
            if let Some(e) = error {
                Err(anyhow!("line: {line}, error: {e}"))
            } else {
                Ok(
                    LineRecord {
                        line,
                        keys,
                        order,
                    }
                )
            }
        }
    }

    pub fn line(self) -> String {
        self.line
    }
}

impl Eq for LineRecord {}

impl PartialEq<Self> for LineRecord {
    fn eq(&self, other: &Self) -> bool {
        self.keys == other.keys
    }
}

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

impl Ord for LineRecord {
    fn cmp(&self, other: &Self) -> Ordering {
        let ordering = self.keys.cmp(&other.keys);
        match ordering {
            Ordering::Less => {
                match &self.order {
                    Order::Asc => {
                        Ordering::Less
                    }
                    Order::Desc => {
                        Ordering::Greater
                    }
                }
            }
            Ordering::Equal => {
                Ordering::Equal
            }
            Ordering::Greater => {
                match &self.order {
                    Order::Asc => {
                        Ordering::Greater
                    }
                    Order::Desc => {
                        Ordering::Less
                    }
                }
            }
        }
    }
}

