use std::fmt;

use decimal::d128;
use serde::ser::{Serialize, SerializeMap, Serializer};
use tarantool::tlua::{self, LuaRead};

use crate::errors::QueryPlannerError;
use crate::executor::vtable::VirtualTable;
use crate::ir::relation::Column;
use crate::ir::value::{AsIrVal, Value as IrValue};

/// `Value` cointains tarantool datatypes
#[derive(LuaRead, Debug, PartialEq, Clone)]
pub enum Value {
    Boolean(bool),
    Number(f64),
    Integer(i64),
    String(String),
    Unsigned(u64),
    Null(tlua::Null),
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self {
            Value::Boolean(v) => write!(f, "{}", v),
            Value::Number(v) => write!(f, "{}", v),
            Value::Integer(v) => write!(f, "{}", v),
            Value::Unsigned(v) => write!(f, "{}", v),
            Value::String(v) => write!(f, "'{}'", v),
            Value::Null(_) => write!(f, "NULL"),
        }
    }
}

/// IR `Value` convertor implementation
impl AsIrVal for Value {
    fn as_ir_value(&self) -> Result<IrValue, QueryPlannerError> {
        match &self {
            Value::Boolean(v) => Ok(IrValue::Boolean(*v)),
            Value::Number(v) => Ok(IrValue::number_from_str(&v.to_string())?),
            Value::Integer(v) => Ok(IrValue::Number(d128::from(*v))),
            Value::String(v) => Ok(IrValue::String(v.clone())),
            Value::Unsigned(v) => Ok(IrValue::Number(d128::from(*v))),
            Value::Null(_) => Ok(IrValue::Null),
        }
    }
}

/// Custom Implementation `ser::Serialize`, because if using standard `#derive[Serialize]` then each `Value` type
/// record serialize as list and it doesn't correct for result format
impl Serialize for Value {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match &self {
            Value::Boolean(v) => serializer.serialize_bool(*v),
            Value::Number(v) => serializer.serialize_f64(*v),
            Value::Integer(v) => serializer.serialize_i64(*v),
            Value::String(v) => serializer.serialize_str(v),
            Value::Unsigned(v) => serializer.serialize_u64(*v),
            Value::Null(_) => serializer.serialize_none(),
        }
    }
}

impl Eq for Value {}

type BoxExecuteTuple = Vec<Value>;

#[derive(LuaRead, Debug, PartialEq, Eq, Clone)]
pub struct BoxExecuteFormat {
    pub metadata: Vec<Column>,
    pub rows: Vec<BoxExecuteTuple>,
}

impl Default for BoxExecuteFormat {
    fn default() -> Self {
        Self::new()
    }
}

impl BoxExecuteFormat {
    /// Create empty query result set
    #[allow(dead_code)]
    pub fn new() -> Self {
        BoxExecuteFormat {
            metadata: Vec::new(),
            rows: Vec::new(),
        }
    }

    /// Merge two record sets. If current recordset is empty function sets metadata and appends result rows.
    /// If the current recordset is not empty compare its metadata with the one from the new recordset.
    /// If they differ return error.
    ///
    ///  # Errors
    ///  - metadata isn't equal.
    #[allow(dead_code)]
    pub fn extend(&mut self, recordset: BoxExecuteFormat) -> Result<(), QueryPlannerError> {
        if self.metadata.is_empty() {
            self.metadata = recordset.clone().metadata;
        }

        if self.metadata != recordset.metadata {
            return Err(QueryPlannerError::CustomError(String::from(
                "Different metadata. BoxExecuteFormat can't be extended",
            )));
        }
        self.rows.extend(recordset.rows);
        Ok(())
    }

    /// Converts result to virtual table for linker
    ///
    /// # Errors
    /// - convert to virtual table error
    #[allow(dead_code)]
    pub fn as_virtual_table(&self) -> Result<VirtualTable, QueryPlannerError> {
        let mut result = VirtualTable::new();

        for col in &self.metadata {
            result.add_column(col.clone());
        }

        for t in &self.rows {
            result.add_tuple(t.clone())?;
        }

        Ok(result)
    }
}

/// Custom Implementation `ser::Serialize`, because if using standard `#derive[Serialize]` then each `BoxExecuteResult`
/// record is serialized to a list. That is not the result we expect.
impl Serialize for BoxExecuteFormat {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut map = serializer.serialize_map(Some(2))?;
        map.serialize_entry("metadata", &self.metadata)?;
        map.serialize_entry("rows", &self.rows)?;

        map.end()
    }
}

#[cfg(test)]
mod tests;