// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#[cfg(not(feature = "std"))]
use alloc::string::String;

use core::{
    fmt,
    ops::{Deref, DerefMut},
};

#[cfg(feature = "bigdecimal")]
use bigdecimal::BigDecimal;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use crate::{ast::Ident, tokenizer::Span};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};

/// Wraps a primitive SQL [`Value`]  with its [`Span`] location
///
/// # Example: create a `ValueWithSpan` from a `Value`
/// ```
/// # use sqlparser::ast::{Value, ValueWithSpan};
/// # use sqlparser::tokenizer::{Location, Span};
/// let value = Value::SingleQuotedString(String::from("endpoint"));
/// // from line 1, column 1 to line 1, column 7
/// let span = Span::new(Location::new(1, 1), Location::new(1, 7));
/// let value_with_span = value.with_span(span);
/// ```
///
/// # Example: create a `ValueWithSpan` from a `Value` with an empty span
///
/// You can call [`Value::with_empty_span`] to create a `ValueWithSpan` with an empty span
/// ```
/// # use sqlparser::ast::{Value, ValueWithSpan};
/// # use sqlparser::tokenizer::{Location, Span};
/// let value = Value::SingleQuotedString(String::from("endpoint"));
/// let value_with_span = value.with_empty_span();
/// assert_eq!(value_with_span.span, Span::empty());
/// ```
///
/// You can also use the [`From`] trait to convert  `ValueWithSpan` to/from `Value`s
/// ```
/// # use sqlparser::ast::{Value, ValueWithSpan};
/// # use sqlparser::tokenizer::{Location, Span};
/// let value = Value::SingleQuotedString(String::from("endpoint"));
/// // converting `Value` to `ValueWithSpan` results in an empty span
/// let value_with_span: ValueWithSpan = value.into();
/// assert_eq!(value_with_span.span, Span::empty());
/// // convert back to `Value`
/// let value: Value = value_with_span.into();
/// ```
/// A `Value` paired with its source `Span` location.
#[derive(Debug, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
    feature = "visitor",
    derive(Visit, VisitMut),
    visit(with = "visit_value")
)]
pub struct ValueWithSpan {
    /// The wrapped `Value`.
    pub value: Value,
    /// The source `Span` covering the token(s) that produced the value.
    pub span: Span,
}

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

impl Ord for ValueWithSpan {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        self.value.cmp(&other.value)
    }
}

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

impl core::hash::Hash for ValueWithSpan {
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        self.value.hash(state);
    }
}

impl From<Value> for ValueWithSpan {
    fn from(value: Value) -> Self {
        value.with_empty_span()
    }
}

impl From<ValueWithSpan> for Value {
    fn from(value: ValueWithSpan) -> Self {
        value.value
    }
}

impl Deref for ValueWithSpan {
    type Target = Value;

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

impl DerefMut for ValueWithSpan {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.value
    }
}

/// Primitive SQL values such as number and string
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Value {
    /// Numeric literal
    #[cfg(not(feature = "bigdecimal"))]
    Number(String, bool),
    #[cfg(feature = "bigdecimal")]
    /// HINT: use `test_utils::number` to make an instance of
    /// Value::Number This might help if you your tests pass locally
    /// but fail on CI with the `--all-features` flag enabled
    /// Numeric literal (uses `BigDecimal` when the `bigdecimal` feature is enabled).
    Number(BigDecimal, bool),
    /// 'string value'
    SingleQuotedString(String),
    /// Dollar-quoted string literal, e.g. `$$...$$` or `$tag$...$tag$` (Postgres syntax).
    DollarQuotedString(DollarQuotedString),
    /// Triple single quoted strings: Example '''abc'''
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
    TripleSingleQuotedString(String),
    /// Triple double quoted strings: Example """abc"""
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
    TripleDoubleQuotedString(String),
    /// e'string value' (postgres extension)
    /// See [Postgres docs](https://www.postgresql.org/docs/8.3/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS)
    /// for more details.
    EscapedStringLiteral(String),
    /// u&'string value' (postgres extension)
    /// See [Postgres docs](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-UESCAPE)
    /// for more details.
    UnicodeStringLiteral(String),
    /// B'string value'
    SingleQuotedByteStringLiteral(String),
    /// B"string value"
    DoubleQuotedByteStringLiteral(String),
    /// Triple single quoted literal with byte string prefix. Example `B'''abc'''`
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
    TripleSingleQuotedByteStringLiteral(String),
    /// Triple double quoted literal with byte string prefix. Example `B"""abc"""`
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
    TripleDoubleQuotedByteStringLiteral(String),
    /// Single quoted literal with raw string prefix. Example `R'abc'`
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
    SingleQuotedRawStringLiteral(String),
    /// Double quoted literal with raw string prefix. Example `R"abc"`
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
    DoubleQuotedRawStringLiteral(String),
    /// Triple single quoted literal with raw string prefix. Example `R'''abc'''`
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
    TripleSingleQuotedRawStringLiteral(String),
    /// Triple double quoted literal with raw string prefix. Example `R"""abc"""`
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
    TripleDoubleQuotedRawStringLiteral(String),
    /// N'string value'
    NationalStringLiteral(String),
    /// Quote delimited literal. Examples `Q'{ab'c}'`, `Q'|ab'c|'`, `Q'|ab|c|'`
    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA)
    QuoteDelimitedStringLiteral(QuoteDelimitedString),
    /// "National" quote delimited literal. Examples `Q'{ab'c}'`, `Q'|ab'c|'`, `Q'|ab|c|'`
    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA)
    NationalQuoteDelimitedStringLiteral(QuoteDelimitedString),
    /// X'hex value'
    HexStringLiteral(String),

    /// Double quoted string literal, e.g. `"abc"`.
    DoubleQuotedString(String),
    /// Boolean value true or false
    Boolean(bool),
    /// `NULL` value
    Null,
    /// `?` or `$` Prepared statement arg placeholder
    Placeholder(String),
}

impl ValueWithSpan {
    /// If the underlying literal is a string, regardless of quote style, returns the associated string value
    pub fn into_string(self) -> Option<String> {
        self.value.into_string()
    }
}

impl Value {
    /// If the underlying literal is a string, regardless of quote style, returns the associated string value
    pub fn into_string(self) -> Option<String> {
        match self {
            Value::SingleQuotedString(s)
            | Value::DoubleQuotedString(s)
            | Value::TripleSingleQuotedString(s)
            | Value::TripleDoubleQuotedString(s)
            | Value::SingleQuotedByteStringLiteral(s)
            | Value::DoubleQuotedByteStringLiteral(s)
            | Value::TripleSingleQuotedByteStringLiteral(s)
            | Value::TripleDoubleQuotedByteStringLiteral(s)
            | Value::SingleQuotedRawStringLiteral(s)
            | Value::DoubleQuotedRawStringLiteral(s)
            | Value::TripleSingleQuotedRawStringLiteral(s)
            | Value::TripleDoubleQuotedRawStringLiteral(s)
            | Value::EscapedStringLiteral(s)
            | Value::UnicodeStringLiteral(s)
            | Value::NationalStringLiteral(s)
            | Value::HexStringLiteral(s) => Some(s),
            Value::DollarQuotedString(s) => Some(s.value),
            Value::QuoteDelimitedStringLiteral(s) => Some(s.value),
            Value::NationalQuoteDelimitedStringLiteral(s) => Some(s.value),
            _ => None,
        }
    }

    /// Attach the provided `span` to this `Value` and return `ValueWithSpan`.
    pub fn with_span(self, span: Span) -> ValueWithSpan {
        ValueWithSpan { value: self, span }
    }

    /// Convenience for attaching an empty span to this `Value`.
    pub fn with_empty_span(self) -> ValueWithSpan {
        self.with_span(Span::empty())
    }
}

impl fmt::Display for ValueWithSpan {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.value)
    }
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Value::Number(v, l) => write!(f, "{}{long}", v, long = if *l { "L" } else { "" }),
            Value::DoubleQuotedString(v) => write!(f, "\"{}\"", escape_double_quote_string(v)),
            Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)),
            Value::TripleSingleQuotedString(v) => {
                write!(f, "'''{v}'''")
            }
            Value::TripleDoubleQuotedString(v) => {
                write!(f, r#""""{v}""""#)
            }
            Value::DollarQuotedString(v) => write!(f, "{v}"),
            Value::EscapedStringLiteral(v) => write!(f, "E'{}'", escape_escaped_string(v)),
            Value::UnicodeStringLiteral(v) => write!(f, "U&'{}'", escape_unicode_string(v)),
            Value::NationalStringLiteral(v) => write!(f, "N'{v}'"),
            Value::QuoteDelimitedStringLiteral(v) => v.fmt(f),
            Value::NationalQuoteDelimitedStringLiteral(v) => write!(f, "N{v}"),
            Value::HexStringLiteral(v) => write!(f, "X'{v}'"),
            Value::Boolean(v) => write!(f, "{v}"),
            Value::SingleQuotedByteStringLiteral(v) => write!(f, "B'{v}'"),
            Value::DoubleQuotedByteStringLiteral(v) => write!(f, "B\"{v}\""),
            Value::TripleSingleQuotedByteStringLiteral(v) => write!(f, "B'''{v}'''"),
            Value::TripleDoubleQuotedByteStringLiteral(v) => write!(f, r#"B"""{v}""""#),
            Value::SingleQuotedRawStringLiteral(v) => write!(f, "R'{v}'"),
            Value::DoubleQuotedRawStringLiteral(v) => write!(f, "R\"{v}\""),
            Value::TripleSingleQuotedRawStringLiteral(v) => write!(f, "R'''{v}'''"),
            Value::TripleDoubleQuotedRawStringLiteral(v) => write!(f, r#"R"""{v}""""#),
            Value::Null => write!(f, "NULL"),
            Value::Placeholder(v) => write!(f, "{v}"),
        }
    }
}

/// A dollar-quoted string literal, e.g. `$$...$$` or `$tag$...$tag$`.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct DollarQuotedString {
    /// Inner string contents.
    pub value: String,
    /// Optional tag used in the opening/closing delimiter.
    pub tag: Option<String>,
}

impl fmt::Display for DollarQuotedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.tag {
            Some(tag) => {
                write!(f, "${}${}${}$", tag, self.value, tag)
            }
            None => {
                write!(f, "$${}$$", self.value)
            }
        }
    }
}

/// A quote delimited string literal, e.g. `Q'_abc_'`.
///
/// See [Value::QuoteDelimitedStringLiteral] and/or
/// [Value::NationalQuoteDelimitedStringLiteral].
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct QuoteDelimitedString {
    /// the quote start character; i.e. the character _after_ the opening `Q'`
    pub start_quote: char,
    /// the string literal value itself
    pub value: String,
    /// the quote end character; i.e. the character _before_ the closing `'`
    pub end_quote: char,
}

impl fmt::Display for QuoteDelimitedString {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Q'{}{}{}'", self.start_quote, self.value, self.end_quote)
    }
}

/// Represents the date/time fields used by functions like `EXTRACT`.
///
/// Each variant corresponds to a supported date/time part (for example
/// `YEAR`, `MONTH`, `DAY`, etc.). The `Custom` variant allows arbitrary
/// identifiers (e.g. dialect-specific abbreviations).
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum DateTimeField {
    /// `YEAR`
    Year,
    /// `YEARS` (plural form)
    Years,
    /// `MONTH`
    Month,
    /// `MONTHS` (plural form)
    Months,
    /// `WEEK`, optionally followed by a weekday, e.g. `WEEK(MONDAY)`.
    ///
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/date_functions#extract)
    Week(Option<Ident>),
    /// `WEEKS` (plural form)
    Weeks,
    /// `DAY`
    Day,
    /// `DAYOFWEEK`
    DayOfWeek,
    /// `DAYOFYEAR`
    DayOfYear,
    /// `DAYS` (plural form)
    Days,
    /// `DATE`
    Date,
    /// `DATETIME`
    Datetime,
    /// `HOUR`
    Hour,
    /// `HOURS` (plural form)
    Hours,
    /// `MINUTE`
    Minute,
    /// `MINUTES` (plural form)
    Minutes,
    /// `SECOND`
    Second,
    /// `SECONDS` (plural form)
    Seconds,
    /// `CENTURY`
    Century,
    /// `DECADE`
    Decade,
    /// `DOW` (day of week short form)
    Dow,
    /// `DOY` (day of year short form)
    Doy,
    /// `EPOCH`
    Epoch,
    /// `ISODOW`
    Isodow,
    /// `ISOYEAR`
    Isoyear,
    /// `ISOWEEK`
    IsoWeek,
    /// `JULIAN`
    Julian,
    /// `MICROSECOND`
    Microsecond,
    /// `MICROSECONDS` (plural form)
    Microseconds,
    /// `MILLENIUM` (alternate spelling)
    Millenium,
    /// `MILLENNIUM` (alternate spelling)
    Millennium,
    /// `MILLISECOND`
    Millisecond,
    /// `MILLISECONDS` (plural form)
    Milliseconds,
    /// `NANOSECOND`
    Nanosecond,
    /// `NANOSECONDS` (plural form)
    Nanoseconds,
    /// `QUARTER`
    Quarter,
    /// `TIME`
    Time,
    /// `TIMEZONE`
    Timezone,
    /// `TIMEZONE_ABBR`
    TimezoneAbbr,
    /// `TIMEZONE_HOUR`
    TimezoneHour,
    /// `TIMEZONE_MINUTE`
    TimezoneMinute,
    /// `TIMEZONE_REGION`
    TimezoneRegion,
    /// `NODATETIME` indicates no date/time part
    NoDateTime,
    /// Arbitrary abbreviation or custom date-time part.
    ///
    /// ```sql
    /// EXTRACT(q FROM CURRENT_TIMESTAMP)
    /// ```
    /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions-date-time#supported-date-and-time-parts)
    Custom(Ident),
}

impl fmt::Display for DateTimeField {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DateTimeField::Year => write!(f, "YEAR"),
            DateTimeField::Years => write!(f, "YEARS"),
            DateTimeField::Month => write!(f, "MONTH"),
            DateTimeField::Months => write!(f, "MONTHS"),
            DateTimeField::Week(week_day) => {
                write!(f, "WEEK")?;
                if let Some(week_day) = week_day {
                    write!(f, "({week_day})")?
                }
                Ok(())
            }
            DateTimeField::Weeks => write!(f, "WEEKS"),
            DateTimeField::Day => write!(f, "DAY"),
            DateTimeField::DayOfWeek => write!(f, "DAYOFWEEK"),
            DateTimeField::DayOfYear => write!(f, "DAYOFYEAR"),
            DateTimeField::Days => write!(f, "DAYS"),
            DateTimeField::Date => write!(f, "DATE"),
            DateTimeField::Datetime => write!(f, "DATETIME"),
            DateTimeField::Hour => write!(f, "HOUR"),
            DateTimeField::Hours => write!(f, "HOURS"),
            DateTimeField::Minute => write!(f, "MINUTE"),
            DateTimeField::Minutes => write!(f, "MINUTES"),
            DateTimeField::Second => write!(f, "SECOND"),
            DateTimeField::Seconds => write!(f, "SECONDS"),
            DateTimeField::Century => write!(f, "CENTURY"),
            DateTimeField::Decade => write!(f, "DECADE"),
            DateTimeField::Dow => write!(f, "DOW"),
            DateTimeField::Doy => write!(f, "DOY"),
            DateTimeField::Epoch => write!(f, "EPOCH"),
            DateTimeField::Isodow => write!(f, "ISODOW"),
            DateTimeField::Isoyear => write!(f, "ISOYEAR"),
            DateTimeField::IsoWeek => write!(f, "ISOWEEK"),
            DateTimeField::Julian => write!(f, "JULIAN"),
            DateTimeField::Microsecond => write!(f, "MICROSECOND"),
            DateTimeField::Microseconds => write!(f, "MICROSECONDS"),
            DateTimeField::Millenium => write!(f, "MILLENIUM"),
            DateTimeField::Millennium => write!(f, "MILLENNIUM"),
            DateTimeField::Millisecond => write!(f, "MILLISECOND"),
            DateTimeField::Milliseconds => write!(f, "MILLISECONDS"),
            DateTimeField::Nanosecond => write!(f, "NANOSECOND"),
            DateTimeField::Nanoseconds => write!(f, "NANOSECONDS"),
            DateTimeField::Quarter => write!(f, "QUARTER"),
            DateTimeField::Time => write!(f, "TIME"),
            DateTimeField::Timezone => write!(f, "TIMEZONE"),
            DateTimeField::TimezoneAbbr => write!(f, "TIMEZONE_ABBR"),
            DateTimeField::TimezoneHour => write!(f, "TIMEZONE_HOUR"),
            DateTimeField::TimezoneMinute => write!(f, "TIMEZONE_MINUTE"),
            DateTimeField::TimezoneRegion => write!(f, "TIMEZONE_REGION"),
            DateTimeField::NoDateTime => write!(f, "NODATETIME"),
            DateTimeField::Custom(custom) => write!(f, "{custom}"),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// The Unicode Standard defines four normalization forms, which are intended to eliminate
/// certain distinctions between visually or functionally identical characters.
///
/// See [Unicode Normalization Forms](https://unicode.org/reports/tr15/) for details.
pub enum NormalizationForm {
    /// Canonical Decomposition, followed by Canonical Composition.
    NFC,
    /// Canonical Decomposition.
    NFD,
    /// Compatibility Decomposition, followed by Canonical Composition.
    NFKC,
    /// Compatibility Decomposition.
    NFKD,
}

impl fmt::Display for NormalizationForm {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            NormalizationForm::NFC => write!(f, "NFC"),
            NormalizationForm::NFD => write!(f, "NFD"),
            NormalizationForm::NFKC => write!(f, "NFKC"),
            NormalizationForm::NFKD => write!(f, "NFKD"),
        }
    }
}

pub struct EscapeQuotedString<'a> {
    string: &'a str,
    quote: char,
}

impl fmt::Display for EscapeQuotedString<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // EscapeQuotedString doesn't know which mode of escape was
        // chosen by the user. So this code must to correctly display
        // strings without knowing if the strings are already escaped
        // or not.
        //
        // If the quote symbol in the string is repeated twice, OR, if
        // the quote symbol is after backslash, display all the chars
        // without any escape. However, if the quote symbol is used
        // just between usual chars, `fmt()` should display it twice."
        //
        // The following table has examples
        //
        // | original query | mode      | AST Node                                           | serialized   |
        // | -------------  | --------- | -------------------------------------------------- | ------------ |
        // | `"A""B""A"`    | no-escape | `DoubleQuotedString(String::from("A\"\"B\"\"A"))`  | `"A""B""A"`  |
        // | `"A""B""A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
        // | `"A\"B\"A"`    | no-escape | `DoubleQuotedString(String::from("A\\\"B\\\"A"))`  | `"A\"B\"A"`  |
        // | `"A\"B\"A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
        let quote = self.quote;
        let mut previous_char = char::default();
        let mut start_idx = 0;
        let mut peekable_chars = self.string.char_indices().peekable();
        while let Some(&(idx, ch)) = peekable_chars.peek() {
            match ch {
                char if char == quote => {
                    if previous_char == '\\' {
                        // the quote is already escaped with a backslash, skip
                        peekable_chars.next();
                        continue;
                    }
                    peekable_chars.next();
                    match peekable_chars.peek() {
                        Some((_, c)) if *c == quote => {
                            // the quote is already escaped with another quote, skip
                            peekable_chars.next();
                        }
                        _ => {
                            // The quote is not escaped.
                            // Including idx in the range, so the quote at idx will be printed twice:
                            // in this call to write_str() and in the next one.
                            f.write_str(&self.string[start_idx..=idx])?;
                            start_idx = idx;
                        }
                    }
                }
                _ => {
                    peekable_chars.next();
                }
            }
            previous_char = ch;
        }
        f.write_str(&self.string[start_idx..])?;
        Ok(())
    }
}

/// Return a helper which formats `string` for inclusion inside a quoted
/// literal that uses `quote` as the delimiter.
pub fn escape_quoted_string(string: &str, quote: char) -> EscapeQuotedString<'_> {
    EscapeQuotedString { string, quote }
}

/// Convenience wrapper for escaping strings for single-quoted literals (`'`).
pub fn escape_single_quote_string(s: &str) -> EscapeQuotedString<'_> {
    escape_quoted_string(s, '\'')
}

/// Convenience wrapper for escaping strings for double-quoted literals (`").`
pub fn escape_double_quote_string(s: &str) -> EscapeQuotedString<'_> {
    escape_quoted_string(s, '\"')
}

pub struct EscapeEscapedStringLiteral<'a>(&'a str);

impl fmt::Display for EscapeEscapedStringLiteral<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for c in self.0.chars() {
            match c {
                '\'' => {
                    write!(f, r#"\'"#)?;
                }
                '\\' => {
                    write!(f, r#"\\"#)?;
                }
                '\n' => {
                    write!(f, r#"\n"#)?;
                }
                '\t' => {
                    write!(f, r#"\t"#)?;
                }
                '\r' => {
                    write!(f, r#"\r"#)?;
                }
                _ => {
                    write!(f, "{c}")?;
                }
            }
        }
        Ok(())
    }
}

/// Return a helper which escapes characters for string literals that use
/// PostgreSQL-style escaped string literals (e.g. `E'...')`.
pub fn escape_escaped_string(s: &str) -> EscapeEscapedStringLiteral<'_> {
    EscapeEscapedStringLiteral(s)
}

pub struct EscapeUnicodeStringLiteral<'a>(&'a str);

impl fmt::Display for EscapeUnicodeStringLiteral<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for c in self.0.chars() {
            match c {
                '\'' => {
                    write!(f, "''")?;
                }
                '\\' => {
                    write!(f, r#"\\"#)?;
                }
                x if x.is_ascii() => {
                    write!(f, "{c}")?;
                }
                _ => {
                    let codepoint = c as u32;
                    // if the character fits in 32 bits, we can use the \XXXX format
                    // otherwise, we need to use the \+XXXXXX format
                    if codepoint <= 0xFFFF {
                        write!(f, "\\{codepoint:04X}")?;
                    } else {
                        write!(f, "\\+{codepoint:06X}")?;
                    }
                }
            }
        }
        Ok(())
    }
}

/// Return a helper which escapes non-ASCII characters using `\XXXX` or
/// `\+XXXXXX` Unicode escape formats (used for `U&'...'` style literals).
pub fn escape_unicode_string(s: &str) -> EscapeUnicodeStringLiteral<'_> {
    EscapeUnicodeStringLiteral(s)
}

/// The side on which `TRIM` should be applied.
///
/// Corresponds to `TRIM(BOTH|LEADING|TRAILING)` SQL syntax.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum TrimWhereField {
    /// `BOTH` (trim from both ends)
    Both,
    /// `LEADING` (trim from start)
    Leading,
    /// `TRAILING` (trim from end)
    Trailing,
}

impl fmt::Display for TrimWhereField {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use TrimWhereField::*;
        f.write_str(match self {
            Both => "BOTH",
            Leading => "LEADING",
            Trailing => "TRAILING",
        })
    }
}
