use std::fmt::{self, Display, Formatter};
use std::str::FromStr;

use crate::err::TranslationErrors;
use crate::sql::expression::identifier::Identifier;
use crate::sql::expression::{Leaf, SQLExpression};
use crate::sql::query::PatternVariable;
use crate::write_utils::pad;
use anyhow::{anyhow, Result};
use chrono::{DateTime, Utc};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BinaryLiteral {
    hex_value: String,
}

impl BinaryLiteral {
    pub fn new(hex_value: &str) -> Self {
        Self {
            hex_value: hex_value.to_string(),
        }
    }
    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        write!(f, "x{}", self.hex_value)
    }
}

impl Display for BinaryLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BooleanLiteral {
    pub value: bool,
}

impl BooleanLiteral {
    pub fn new(value: bool) -> Self {
        Self { value }
    }

    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        if self.value {
            write!(f, "true")
        } else {
            write!(f, "false")
        }
    }
}

impl Display for BooleanLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ColumnReference {
    pub identifier: Identifier,
}

impl ColumnReference {
    pub fn new(identifier: Identifier) -> Self {
        Self { identifier }
    }

    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        indentation: usize,
    ) -> std::fmt::Result {
        self.identifier.fmt_indented(f, indentation)
    }

    pub fn prefix(&self, prefix: &[&str]) -> Self {
        Self {
            identifier: self.identifier.prefix_from_strings(&prefix).into(),
        }
    }
}

impl Display for ColumnReference {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

impl FromStr for ColumnReference {
    type Err = TranslationErrors;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        s.parse().map(|ident| Self::new(ident))
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PatternVariableReference {
    pub pattern_variable: PatternVariable,
}

impl PatternVariableReference {
    pub fn new(pattern_variable: PatternVariable) -> Self {
        Self { pattern_variable }
    }

    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        self.pattern_variable.fmt(f)
    }
}

impl Display for PatternVariableReference {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DecimalLiteral {
    pub number: String,
}

impl DecimalLiteral {
    pub fn new(number: &str) -> Self {
        Self {
            number: number.to_string(),
        }
    }
    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        write!(f, "{}", self.number.clone())
    }
}

impl Display for DecimalLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IntegerLiteral {
    pub number: String,
}

impl IntegerLiteral {
    pub fn from_int(number: i64) -> Self {
        Self {
            number: number.to_string(),
        }
    }

    pub fn new(number: &str) -> Self {
        Self {
            number: number.to_string(),
        }
    }

    pub fn to_sql_expression(self) -> SQLExpression {
        SQLExpression::Leaf(Leaf::IntegerLiteral(self))
    }

    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        write!(f, "{}", self.number.clone())
    }
}

impl Display for IntegerLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct NullLiteral {}

impl NullLiteral {
    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        write!(f, "NULL")
    }
}

impl Display for NullLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Unit {
    Year,
    Month,
    Week,
    Day,
    Hour,
    Minute,
    Second,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IntervalLiteral {
    value: String,
    unit: Unit,
}

impl IntervalLiteral {
    pub fn new<V: ToString>(value: V, unit: Unit) -> Self {
        Self {
            value: value.to_string(),
            unit,
        }
    }
    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        write!(
            f,
            "INTERVAL '{}' {}",
            self.value,
            match self.unit {
                Unit::Year => "YEAR",
                Unit::Month => "MONTH",
                Unit::Week => "WEEK",
                Unit::Day => "DAY",
                Unit::Hour => "HOUR",
                Unit::Minute => "MINUTE",
                Unit::Second => "SECOND",
            }
        )
    }
}

impl Display for IntervalLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScientificLiteral {
    number: String,
}

impl ScientificLiteral {
    pub fn new(number: &str) -> Self {
        Self {
            number: number.to_string(),
        }
    }
    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        write!(f, "{}", self.number)
    }
}

impl Display for ScientificLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StringLiteral {
    pub value: String,
}

impl StringLiteral {
    pub fn new(value: &str) -> Self {
        Self {
            value: value.to_string(),
        }
    }

    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        write!(f, "'{}'", self.value.replace("'", "''"))
    }
}

impl Display for StringLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TimestampLiteral {
    value: DateTime<Utc>,
}

impl TimestampLiteral {
    pub fn new(value: DateTime<Utc>) -> Self {
        Self { value }
    }

    pub fn from_string_literal(literal: &StringLiteral) -> anyhow::Result<Self> {
        TimestampLiteral::parse(&literal.value).map(|value| Self { value })
    }

    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        write!(f, "TIMESTAMP '{}'", self.value)
    }

    fn parse(literal: &str) -> anyhow::Result<DateTime<Utc>> {
        DateTime::parse_from_rfc3339(literal)
            .or(DateTime::parse_from_rfc2822(literal))
            .map(|dt| dt.with_timezone(&Utc))
            .map_err(|e| anyhow!("Could not parse timestamp literal: {}", e))
    }
}

impl Display for TimestampLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnicodeStringLiteral {
    value: String,
    uescape: Option<String>,
}

impl UnicodeStringLiteral {
    pub fn new<T: AsRef<str>>(value: &str, uescape: Option<T>) -> Self {
        Self {
            value: value.to_string(),
            uescape: uescape.map(|u| u.as_ref().to_string()),
        }
    }

    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _indentation: usize,
    ) -> std::fmt::Result {
        let qesc_value = self.value.replace("'", "''");
        let qesc_uesc = self
            .uescape
            .as_ref()
            .map(|u| u.replace("'", "''"))
            .map(|u| format!(" UESCAPE '{}'", u));
        write!(
            f,
            "U&'{}'{}",
            qesc_value,
            qesc_uesc.unwrap_or("".to_string())
        )
    }
}

impl Display for UnicodeStringLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArrayLiteral {
    pub elements: Vec<SQLExpression>,
}

impl ArrayLiteral {
    pub fn new(elements: Vec<SQLExpression>) -> Self {
        Self { elements }
    }

    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        indentation: usize,
    ) -> std::fmt::Result {
        let flat = self.subelements() <= 3;

        if flat {
            write!(f, "ARRAY[")?;
            for (i, element) in self.elements.iter().enumerate() {
                element.fmt_indented(f, indentation)?;
                if i < self.elements.len() - 1 {
                    write!(f, ", ")?;
                }
            }
        } else {
            writeln!(f, "ARRAY[")?;
            for (i, element) in self.elements.iter().enumerate() {
                pad(f, indentation + 4)?;
                element.fmt_indented(f, indentation + 4)?;
                if i < self.elements.len() - 1 {
                    writeln!(f, ",")?;
                }
            }
            writeln!(f)?;
            pad(f, indentation)?;
        }
        write!(f, "]")
    }

    pub(crate) fn subelements(&self) -> usize {
        self.elements
            .iter()
            .map(|element| element.subelements() + 1)
            .sum()
    }
}

impl Display for ArrayLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RowLiteral {
    pub values: Vec<SQLExpression>,
}

impl RowLiteral {
    pub fn new(values: Vec<SQLExpression>) -> Self {
        Self { values }
    }

    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        let flat = self.subelements() <= 3;

        if flat {
            write!(f, "ROW(")?;
            for (i, value) in self.values.iter().enumerate() {
                value.fmt_indented(f, indentation)?;
                if i < self.values.len() - 1 {
                    write!(f, ", ")?;
                }
            }
        } else {
            writeln!(f, "ROW(")?;
            for (i, value) in self.values.iter().enumerate() {
                pad(f, indentation + 4)?;
                value.fmt_indented(f, indentation + 4)?;
                if i < self.values.len() - 1 {
                    writeln!(f, ",")?;
                }
            }
            writeln!(f)?;
            pad(f, indentation)?;
        }
        write!(f, ")")
    }

    pub(crate) fn subelements(&self) -> usize {
        self.values
            .iter()
            .map(|value| value.subelements() + 1)
            .sum()
    }
}

impl Display for RowLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TupleLiteral {
    pub elements: Vec<SQLExpression>,
}

impl TupleLiteral {
    pub fn new(elements: Vec<SQLExpression>) -> Self {
        Self { elements }
    }

    pub fn fmt_indented(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        indentation: usize,
    ) -> std::fmt::Result {
        let flat = self.subelements() <= 3;

        if flat {
            write!(f, "(")?;
            for (i, element) in self.elements.iter().enumerate() {
                element.fmt_indented(f, indentation)?;
                if i < self.elements.len() - 1 {
                    write!(f, ", ")?;
                }
            }
        } else {
            writeln!(f, "(")?;
            for (i, element) in self.elements.iter().enumerate() {
                pad(f, indentation + 4)?;
                element.fmt_indented(f, indentation + 4)?;
                if i < self.elements.len() - 1 {
                    writeln!(f, ",")?;
                }
            }
            writeln!(f)?;
            pad(f, indentation)?;
        }
        write!(f, ")")
    }

    pub(crate) fn subelements(&self) -> usize {
        self.elements
            .iter()
            .map(|element| element.subelements() + 1)
            .sum()
    }
}

impl Display for TupleLiteral {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0)
    }
}
