use crate::error::QdrantError::BadRequest;
use crate::error::{QdrantError, Result};
use crate::protocol::{Payload, PointIdType};
use fts_index::protocol::{BooleanQuery, BooleanQueryOccur, Field, Query, TermQuery, TermValue};
use fts_index::tokenizer::tokenize;
use log::*;
use model::collection::{
    get_payload_key, get_tokens_column_name, restore_tokens_column_name, CollectionRecord,
    CollectionRecordPredicateBuilder,
};
use model::meta::tenant::path_to_column_and_idx_name;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Formatter};
use std::vec;

use super::index::PayloadIndexes;
use crate::protocol::Record;
use serde_json::Value;
use sqlx::{types::chrono::NaiveDateTime, FromRow};

#[derive(Debug, Default)]
pub(crate) struct PointQueryParameters {
    pub(crate) must: Vec<(String, ValuePredicate)>,
    pub(crate) should: Vec<(String, ValuePredicate)>,
    pub(crate) must_not: Vec<(String, ValuePredicate)>,
}

impl PointQueryParameters {
    pub(crate) fn apply_predicates(
        &self,
        predicate: &mut CollectionRecordPredicateBuilder,
        without_text: bool,
    ) {
        predicate.apply_filters(self.sql(without_text));
    }

    fn sql(&self, without_text: bool) -> Option<String> {
        let mut musts = vec![];
        for (k, v) in &self.must {
            if let Some(s) = predicate_prepare(k, v, without_text) {
                musts.push(s);
            }
        }

        let mut shoulds = vec![];
        for (k, v) in &self.should {
            if let Some(s) = predicate_prepare(k, v, without_text) {
                shoulds.push(s);
            }
        }

        let mut must_nots = vec![];
        for (k, v) in &self.must_not {
            if let Some(s) = predicate_prepare(k, v, without_text) {
                must_nots.push(s);
            }
        }

        let mut sql = vec![];
        if !musts.is_empty() {
            sql.push(format!("({})", musts.join(" AND ")));
        }
        if !shoulds.is_empty() {
            sql.push(format!("({})", shoulds.join(" OR ")));
        }
        if !must_nots.is_empty() {
            sql.push(format!("({})", must_nots.join(" AND ")));
        }

        if sql.is_empty() {
            None
        } else {
            Some(sql.join(" AND "))
        }
    }
}

impl From<&PointQueryParameters> for Option<Query> {
    fn from(val: &PointQueryParameters) -> Option<Query> {
        let mut must_queries = vec![];
        for (k, v) in &val.must {
            if let ValuePredicate::Text(text) = v {
                text.iter().for_each(|t| {
                    must_queries.push(Query::Term(TermQuery {
                        field: Field::Content,
                        value: TermValue::String(t.clone()),
                    }));
                });
                must_queries.push(Query::Term(TermQuery {
                    field: Field::TextFieldName,
                    value: TermValue::String(restore_tokens_column_name(k)),
                }));
            }
        }

        let mut should_queries = vec![];
        for (k, v) in &val.should {
            if let ValuePredicate::Text(text) = v {
                text.iter().for_each(|t| {
                    should_queries.push(Query::Term(TermQuery {
                        field: Field::Content,
                        value: TermValue::String(t.clone()),
                    }));
                });
                should_queries.push(Query::Term(TermQuery {
                    field: Field::TextFieldName,
                    value: TermValue::String(restore_tokens_column_name(k)),
                }));
            }
        }

        let mut must_not_queries = vec![];
        for (k, v) in &val.must_not {
            if let ValuePredicate::Text(text) = v {
                text.iter().for_each(|t| {
                    must_not_queries.push(Query::Term(TermQuery {
                        field: Field::Content,
                        value: TermValue::String(t.clone()),
                    }));
                });
                must_not_queries.push(Query::Term(TermQuery {
                    field: Field::TextFieldName,
                    value: TermValue::String(restore_tokens_column_name(k)),
                }));
            }
        }

        if must_queries.is_empty() && should_queries.is_empty() && must_not_queries.is_empty() {
            return None;
        }

        let mut queries = vec![];
        if !must_queries.is_empty() {
            queries.push(Query::Boolean(BooleanQuery {
                occur: BooleanQueryOccur::Must,
                queries: must_queries,
            }));
        }
        if !should_queries.is_empty() {
            queries.push(Query::Boolean(BooleanQuery {
                occur: BooleanQueryOccur::Should,
                queries: should_queries,
            }));
        }
        if !must_not_queries.is_empty() {
            queries.push(Query::Boolean(BooleanQuery {
                occur: BooleanQueryOccur::MustNot,
                queries: must_not_queries,
            }));
        }

        Some(Query::Boolean(BooleanQuery {
            occur: BooleanQueryOccur::Must,
            queries,
        }))
    }
}

impl TryFrom<Filter> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(filter: Filter) -> Result<Self> {
        let must = filter
            .must
            .unwrap_or_default()
            .into_iter()
            .map(|condition| condition.try_into())
            .collect::<Result<Vec<(String, ValuePredicate)>>>()?;
        debug!("Must: {:?}", must);
        let should = filter
            .should
            .unwrap_or_default()
            .into_iter()
            .map(|condition| condition.try_into())
            .collect::<Result<Vec<(String, ValuePredicate)>>>()?;
        debug!("Should: {:?}", should);
        let must_not = filter
            .must_not
            .unwrap_or_default()
            .into_iter()
            .map(|condition| condition.try_into())
            .collect::<Result<Vec<(String, ValuePredicate)>>>()?;
        debug!("MustNot: {:?}", must_not);
        Ok(Self {
            must,
            should,
            must_not,
        })
    }
}

impl TryFrom<(Filter, PayloadIndexes)> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(value: (Filter, PayloadIndexes)) -> Result<Self> {
        let filter = value.0;
        let must = filter
            .must
            .unwrap_or_default()
            .into_iter()
            .map(|condition| {
                condition
                    .without_index(!value.1.exists(&condition.key))
                    .try_into()
            })
            .collect::<Result<Vec<(String, ValuePredicate)>>>()?;
        debug!("Must: {:?}", must);
        let should = filter
            .should
            .unwrap_or_default()
            .into_iter()
            .map(|condition| {
                condition
                    .without_index(!value.1.exists(&condition.key))
                    .try_into()
            })
            .collect::<Result<Vec<(String, ValuePredicate)>>>()?;
        debug!("Should: {:?}", should);
        let must_not = filter
            .must_not
            .unwrap_or_default()
            .into_iter()
            .map(|condition| {
                condition
                    .without_index(!value.1.exists(&condition.key))
                    .try_into()
            })
            .collect::<Result<Vec<(String, ValuePredicate)>>>()?;
        debug!("MustNot: {:?}", must_not);
        Ok(Self {
            must,
            should,
            must_not,
        })
    }
}

impl TryFrom<(Filter, Option<String>)> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(value: (Filter, Option<String>)) -> Result<Self> {
        let indexes: PayloadIndexes = value.1.unwrap_or_default().try_into()?;
        (value.0, indexes).try_into()
    }
}

impl TryFrom<Vec<PointIdType>> for PointQueryParameters {
    type Error = QdrantError;
    fn try_from(value: Vec<PointIdType>) -> Result<Self> {
        if value.is_empty() {
            warn!("Empty list of point IDs");
            return Err(QdrantError::BadRequestWithMsg(
                "Empty list of point IDs".to_string(),
            ));
        }
        Ok(Self {
            must: value
                .into_iter()
                .map(|id| ("id".to_string(), ValuePredicate::Keyword(id.to_string())))
                .collect(),
            ..Default::default()
        })
    }
}

impl TryFrom<PointIdType> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(value: PointIdType) -> Result<Self> {
        Ok(Self {
            must: Vec::from_iter(vec![(
                "id".to_string(),
                ValuePredicate::Keyword(value.to_string()),
            )]),
            ..Default::default()
        })
    }
}

impl TryFrom<(Option<Vec<PointIdType>>, Option<Filter>)> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(value: (Option<Vec<PointIdType>>, Option<Filter>)) -> Result<Self> {
        match value {
            (Some(points), None) => points.try_into(),
            (None, Some(filter)) => filter.try_into(),
            _ => {
                warn!("Invalid combination of points and filter");
                Err(BadRequest)
            }
        }
    }
}

impl TryFrom<PointsSelector> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(value: PointsSelector) -> Result<Self> {
        match value {
            PointsSelector::PointIdsSelector(points) => points.points.try_into(),
            PointsSelector::FilterSelector(filter) => filter.filter.try_into(),
        }
    }
}

impl TryFrom<(PointsSelector, PayloadIndexes)> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(value: (PointsSelector, PayloadIndexes)) -> Result<Self> {
        match value.0 {
            PointsSelector::PointIdsSelector(points) => points.points.try_into(),
            PointsSelector::FilterSelector(filter) => (filter.filter, value.1).try_into(),
        }
    }
}

impl TryFrom<(PointsSelector, Option<String>)> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(value: (PointsSelector, Option<String>)) -> Result<Self> {
        let indexes: PayloadIndexes = value.1.unwrap_or_default().try_into()?;
        (value.0, indexes).try_into()
    }
}

impl TryFrom<SetPayloadRequest> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(value: SetPayloadRequest) -> Result<Self> {
        (value.points, value.filter).try_into()
    }
}

impl TryFrom<DeletePayloadRequest> for PointQueryParameters {
    type Error = QdrantError;

    fn try_from(value: DeletePayloadRequest) -> std::result::Result<Self, Self::Error> {
        (value.points, value.filter).try_into()
    }
}

#[macro_export]
macro_rules! bind_query_params {
    ($params:expr, $query:expr, $without_text:expr) => {{
        let mut query = $query;
        for (_, v) in &$params.must {
            query = match v {
                ValuePredicate::Keyword(s) => query.bind(s),
                ValuePredicate::Integer(i) => query.bind(i),
                ValuePredicate::Bool(b) => query.bind(b),
                ValuePredicate::Float(f) => query.bind(f),
                ValuePredicate::Text(t) => match $without_text {
                    true => query,
                    false => query.bind(serde_json::to_string(t).unwrap()),
                },
                ValuePredicate::Orient(o) => match o {
                    PredicateOrient::AnyString(s) => {
                        query = s.iter().fold(query, |q, s| q.bind(s));
                        query
                    }
                    PredicateOrient::AnyInteger(i) => {
                        query = i.iter().fold(query, |q, i| q.bind(i));
                        query
                    }
                    PredicateOrient::ExceptString(s) => {
                        query = s.iter().fold(query, |q, s| q.bind(s));
                        query
                    }
                    PredicateOrient::ExceptInteger(i) => {
                        query = i.iter().fold(query, |q, i| q.bind(i));
                        query
                    }
                },
            }
        }
        for (_, v) in &$params.should {
            query = match v {
                ValuePredicate::Keyword(s) => query.bind(s),
                ValuePredicate::Integer(i) => query.bind(i),
                ValuePredicate::Bool(b) => query.bind(b),
                ValuePredicate::Float(f) => query.bind(f),
                ValuePredicate::Text(t) => match $without_text {
                    true => query,
                    false => query.bind(serde_json::to_string(t).unwrap()),
                },
                ValuePredicate::Orient(o) => match o {
                    PredicateOrient::AnyString(s) => {
                        query = s.iter().fold(query, |q, s| q.bind(s));
                        query
                    }
                    PredicateOrient::AnyInteger(i) => {
                        query = i.iter().fold(query, |q, i| q.bind(i));
                        query
                    }
                    PredicateOrient::ExceptString(s) => {
                        query = s.iter().fold(query, |q, s| q.bind(s));
                        query
                    }
                    PredicateOrient::ExceptInteger(i) => {
                        query = i.iter().fold(query, |q, i| q.bind(i));
                        query
                    }
                },
            }
        }
        for (_, v) in &$params.must_not {
            query = match v {
                ValuePredicate::Keyword(s) => query.bind(s),
                ValuePredicate::Integer(i) => query.bind(i),
                ValuePredicate::Bool(b) => query.bind(b),
                ValuePredicate::Float(f) => query.bind(f),
                ValuePredicate::Text(t) => match $without_text {
                    true => query,
                    false => query.bind(serde_json::to_string(t).unwrap()),
                },
                ValuePredicate::Orient(o) => match o {
                    PredicateOrient::AnyString(s) => {
                        query = s.iter().fold(query, |q, s| q.bind(s));
                        query
                    }
                    PredicateOrient::AnyInteger(i) => {
                        query = i.iter().fold(query, |q, i| q.bind(i));
                        query
                    }
                    PredicateOrient::ExceptString(s) => {
                        query = s.iter().fold(query, |q, s| q.bind(s));
                        query
                    }
                    PredicateOrient::ExceptInteger(i) => {
                        query = i.iter().fold(query, |q, i| q.bind(i));
                        query
                    }
                },
            }
        }
        query
    }};
}
pub(crate) use bind_query_params;

fn predicate_prepare(key: &str, value: &ValuePredicate, without_text: bool) -> Option<String> {
    if matches!(value, ValuePredicate::Text(_)) && without_text {
        return None;
    }

    let e_key = if key.contains("->>") {
        key
    } else {
        &format!("`{}`", key)
    };

    let v = match value {
        ValuePredicate::Text(_) => format!("JSON_CONTAINS(`{}`, ?)", key),
        ValuePredicate::Orient(orient) => match orient {
            PredicateOrient::AnyString(vals) => {
                format!(
                    "{} IN ({})",
                    e_key,
                    vals.iter().map(|_| "?").collect::<Vec<_>>().join(", ")
                )
            }
            PredicateOrient::AnyInteger(vals) => {
                format!(
                    "{} IN ({})",
                    e_key,
                    vals.iter().map(|_| "?").collect::<Vec<_>>().join(", ")
                )
            }
            PredicateOrient::ExceptString(vals) => {
                format!(
                    "{} NOT IN ({})",
                    e_key,
                    vals.iter().map(|_| "?").collect::<Vec<_>>().join(", ")
                )
            }
            PredicateOrient::ExceptInteger(vals) => {
                format!(
                    "{} NOT IN ({})",
                    e_key,
                    vals.iter().map(|_| "?").collect::<Vec<_>>().join(", ")
                )
            }
        },
        _ => format!("{} = ?", e_key),
    };
    Some(v)
}

#[derive(Debug, Deserialize)]
pub struct DeletePayloadRequest {
    pub keys: Vec<String>,
    pub points: Option<Vec<PointIdType>>,
    pub filter: Option<Filter>,
}

#[derive(Debug, Serialize)]
pub(crate) struct PointsCount {
    count: u64,
}

impl From<u64> for PointsCount {
    fn from(count: u64) -> Self {
        Self { count }
    }
}

impl From<PointsCount> for u64 {
    fn from(count: PointsCount) -> Self {
        count.count
    }
}

#[derive(Debug, Clone, Deserialize)]
pub(crate) struct SetPayloadRequest {
    pub(crate) payload: Payload,
    pub(crate) points: Option<Vec<PointIdType>>,
    pub(crate) filter: Option<Filter>,
    pub(crate) key: Option<String>,
}

pub(crate) type DenseVector = Vec<f32>;

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct Batch {
    pub(crate) ids: Vec<PointIdType>,
    pub(crate) vectors: Vec<DenseVector>,
    pub(crate) payloads: Vec<Option<Payload>>,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub(crate) struct PointsBatch {
    pub(crate) batch: Batch,
}

#[derive(Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct PointStruct {
    /// Point id
    pub(crate) id: PointIdType,
    /// Vectors
    #[serde(alias = "vectors")]
    pub(crate) vector: DenseVector,
    /// Payload values (optional)
    pub(crate) payload: Option<Payload>,
}

impl From<&CollectionRecord> for PointStruct {
    fn from(record: &CollectionRecord) -> Self {
        Self {
            id: match record.id.parse::<u64>() {
                Ok(id) => PointIdType::NumId(id),
                Err(_) => PointIdType::Uuid(record.id.clone()),
            },
            vector: match &record.vector {
                Some(v) => match serde_json::from_str(v) {
                    Ok(v) => v,
                    Err(e) => {
                        debug!("Failed to parse vector: {}", e);
                        vec![]
                    }
                },
                None => vec![],
            },
            payload: match record.payload {
                Some(ref p) => match serde_json::from_str(p) {
                    Ok(p) => Some(p),
                    Err(e) => {
                        debug!("Failed to parse payload: {}", e);
                        None
                    }
                },
                None => None,
            },
        }
    }
}

impl From<&Record> for PointStruct {
    fn from(record: &Record) -> Self {
        Self {
            id: record.id.clone(),
            vector: record.vector.clone().unwrap(),
            payload: record.payload.clone(),
        }
    }
}

impl std::fmt::Debug for PointStruct {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("PointStruct")
            .field("id", &self.id)
            .field(
                "vector",
                &format_args!("DenseVector(len={})", self.vector.len()),
            )
            .field("payload", &self.payload)
            .finish()
    }
}

impl PointStruct {
    pub(crate) fn id(&self) -> String {
        self.id.to_string()
    }

    pub(crate) fn vector(&self) -> String {
        format!("{:?}", self.vector)
    }

    pub(crate) fn payload(&self) -> Result<Option<String>> {
        self.payload
            .as_ref()
            .map(|v| v.clone().try_into())
            .transpose()
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub(crate) struct PointsList {
    pub(crate) points: Vec<PointStruct>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(untagged)]
pub(crate) enum PointInsertOperations {
    /// Inset points from a batch.
    PointsBatch(PointsBatch),
    /// Insert points from a list
    PointsList(PointsList),
}

impl From<Vec<CollectionRecord>> for PointInsertOperations {
    fn from(records: Vec<CollectionRecord>) -> Self {
        let points = records
            .into_iter()
            .map(|record| PointStruct::from(&record))
            .collect();
        PointInsertOperations::PointsList(PointsList { points })
    }
}

#[derive(Debug, Deserialize)]
pub(crate) struct PointsApiQueryParams {
    pub(crate) _timeout: Option<u64>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged, rename_all = "snake_case")]
pub(crate) enum PointsSelector {
    /// Select points by list of IDs
    PointIdsSelector(PointIdsList),
    /// Select points by filtering condition
    FilterSelector(FilterSelector),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct PointIdsList {
    pub(crate) points: Vec<PointIdType>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct FilterSelector {
    pub(crate) filter: Filter,
}

#[derive(Debug, Deserialize, Serialize)]
pub(crate) struct PointRequest {
    /// Look for points with ids
    pub(crate) ids: Vec<PointIdType>,
    #[serde(default)]
    pub(crate) with_payload: bool,
    #[serde(default)]
    pub(crate) with_vector: bool,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields, rename_all = "snake_case")]
pub(crate) struct Filter {
    /// All conditions must match
    pub(crate) must: Option<Vec<FieldCondition>>,

    /// At least one condition must match
    pub(crate) should: Option<Vec<FieldCondition>>,

    /// All conditions must not match
    pub(crate) must_not: Option<Vec<FieldCondition>>,
}

/// Scroll request - paginate over all points which matches given condition
#[derive(Clone, Debug, Deserialize, Serialize, Default)]
#[serde(rename_all = "snake_case")]
pub(crate) struct ScrollRequest {
    pub(crate) limit: Option<u32>,
    pub(crate) filter: Option<Filter>,
    #[serde(default)]
    pub(crate) with_payload: bool,
    #[serde(default)]
    pub(crate) with_vector: bool,
    pub(crate) offset: Option<PointIdType>,
}

impl std::fmt::Display for ScrollRequest {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        serde_json::to_string(self)
            .map_err(|_| fmt::Error)
            .and_then(|s| write!(f, "{}", s))
    }
}

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct SearchRequest {
    pub(crate) vector: DenseVector,
    pub(crate) filter: Option<Filter>,
    #[serde(alias = "top")]
    pub(crate) limit: Option<u32>,
    pub(crate) offset: Option<u64>,
    #[serde(default)]
    pub(crate) with_payload: bool,
    #[serde(default)]
    pub(crate) with_vector: bool,
    #[serde(default)]
    pub(crate) score_threshold: f32,
}

impl std::fmt::Debug for SearchRequest {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("SearchRequest")
            .field(
                "vector",
                &format_args!("DenseVector(len={})", self.vector.len()),
            )
            .field("filter", &self.filter)
            .field("limit", &self.limit)
            .field("offset", &self.offset)
            .field("with_payload", &self.with_payload)
            .field("with_vector", &self.with_vector)
            .field("score_threshold", &self.score_threshold)
            .finish()
    }
}

/// All possible payload filtering conditions
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct FieldCondition {
    /// Payload key
    pub(crate) key: String,
    /// Check if point has field with a given value
    pub(crate) r#match: Match,

    #[serde(skip)]
    pub(crate) without_index: bool,
}

impl TryInto<(String, ValuePredicate)> for FieldCondition {
    type Error = QdrantError;

    fn try_into(self) -> Result<(String, ValuePredicate)> {
        if self.key == "id" {
            return Ok(("id".to_string(), self.r#match.try_into()?));
        }

        if let Match::Text(_) = self.r#match {
            let key = match self.without_index {
                true => {
                    warn!("The field on MatchText must have a text index");
                    return Err(QdrantError::BadRequestWithMsg(
                        "The field on MatchText must have a text index".to_string(),
                    ));
                }
                false => get_tokens_column_name(&self.key)?,
            };
            return Ok((key, self.r#match.try_into()?));
        }

        if self.without_index {
            return Ok((get_payload_key(&self.key)?, self.r#match.try_into()?));
        }

        // If we have index, we should use it
        let (path, _) = path_to_column_and_idx_name(&self.key)?;
        Ok((path, self.r#match.try_into()?))
    }
}

impl FieldCondition {
    pub(crate) fn without_index(&self, without: bool) -> Self {
        Self {
            without_index: without,
            ..self.clone()
        }
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub(crate) enum ValueVariants {
    String(String),
    Integer(i64),
    Bool(bool),
}

/// Exact match of the given value
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct MatchValue {
    pub(crate) value: ValueVariants,
}

/// Exact match of the given value
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct MatchText {
    pub(crate) text: String,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
#[serde(untagged)]
pub(crate) enum AnyVariants {
    String(Vec<String>),
    Integer(Vec<i64>),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct MatchAny {
    pub(crate) any: AnyVariants,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct MatchExcept {
    pub(crate) except: AnyVariants,
}

/// Match filter request
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub(crate) enum Match {
    Value(MatchValue),
    Text(MatchText),
    Any(MatchAny),
    Except(MatchExcept),
}

impl TryInto<MatchText> for Match {
    type Error = QdrantError;

    fn try_into(self) -> Result<MatchText> {
        if let Match::Text(t) = self {
            Ok(t)
        } else {
            warn!("Failed to convert Match to MatchText: {:?}", self);
            Err(BadRequest)
        }
    }
}

impl TryInto<MatchValue> for Match {
    type Error = QdrantError;

    fn try_into(self) -> Result<MatchValue> {
        if let Match::Value(v) = self {
            Ok(v)
        } else {
            warn!("Failed to convert Match to MatchValue: {:?}", self);
            Err(BadRequest)
        }
    }
}

impl TryInto<ValuePredicate> for Match {
    type Error = QdrantError;

    fn try_into(self) -> Result<ValuePredicate> {
        match self {
            Match::Value(v) => match v.value {
                ValueVariants::String(s) => Ok(ValuePredicate::Keyword(s)),
                ValueVariants::Integer(i) => Ok(ValuePredicate::Integer(i)),
                ValueVariants::Bool(b) => Ok(ValuePredicate::Bool(b)),
            },
            Match::Text(t) => Ok(ValuePredicate::Text(tokenize(&t.text, 2, 20))),
            Match::Any(a) => match a.any {
                AnyVariants::String(s) => Ok(ValuePredicate::Orient(PredicateOrient::AnyString(s))),
                AnyVariants::Integer(i) => {
                    Ok(ValuePredicate::Orient(PredicateOrient::AnyInteger(i)))
                }
            },
            Match::Except(e) => match e.except {
                AnyVariants::String(s) => {
                    Ok(ValuePredicate::Orient(PredicateOrient::ExceptString(s)))
                }
                AnyVariants::Integer(i) => {
                    Ok(ValuePredicate::Orient(PredicateOrient::ExceptInteger(i)))
                }
            },
        }
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) enum PredicateOrient {
    AnyString(Vec<String>),
    AnyInteger(Vec<i64>),
    ExceptString(Vec<String>),
    ExceptInteger(Vec<i64>),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) enum ValuePredicate {
    Orient(PredicateOrient),
    Keyword(String),
    Integer(i64),
    Bool(bool),
    Float(f64),
    Text(Vec<String>),
}

#[derive(FromRow)]
pub(crate) struct GenericPointModel {
    pub(crate) id: String,
    #[sqlx(default)]
    vector: Option<String>,
    #[sqlx(default)]
    pub(crate) distance: Option<f32>,
    #[sqlx(default)]
    pub(crate) version: Option<u64>,
    #[sqlx(default)]
    payload: Option<sqlx::types::Json<Value>>,
    #[sqlx(default)]
    pub(crate) deleted_at: Option<NaiveDateTime>,
}

impl From<GenericPointModel> for Record {
    fn from(point: GenericPointModel) -> Self {
        Self {
            id: PointIdType::Uuid(point.id),
            payload: point
                .payload
                .map(|p| p.try_into().expect("Failed to parse payload")),
            vector: point
                .vector
                .as_ref()
                .and_then(|v| serde_json::de::from_str(v).ok()),
        }
    }
}

impl From<GenericPointModel> for ScoredPoint {
    fn from(point: GenericPointModel) -> Self {
        Self {
            id: PointIdType::Uuid(point.id),
            version: point.version.expect("Missing required column `version`"),
            score: 1.0 - point.distance.expect("Missing required column `distance`"),
            payload: point
                .payload
                .map(|p| p.try_into().expect("Failed to parse payload")),
            vector: point
                .vector
                .as_ref()
                .and_then(|v| serde_json::de::from_str(v).ok()),
        }
    }
}

/// Result of the points read request
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct ScrollResult {
    /// List of retrieved points
    pub(crate) points: Vec<Record>,
    /// Offset which should be used to retrieve a next page result
    pub(crate) next_page_offset: Option<PointIdType>,
}

/// Search result
#[derive(Serialize, Deserialize)]
pub(crate) struct ScoredPoint {
    /// Point id
    pub(crate) id: PointIdType,
    /// Point version
    pub(crate) version: u64,
    /// Points vector distance to the query vector
    pub(crate) score: f32,
    /// Payload - values assigned to the point
    pub(crate) payload: Option<Payload>,
    /// Vector of the point
    pub(crate) vector: Option<DenseVector>,
}

impl fmt::Debug for ScoredPoint {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("ScoredPoint")
            .field("id", &self.id)
            .field("version", &self.version)
            .field("score", &self.score)
            .field("payload", &self.payload)
            .field(
                "vector",
                &format!(
                    "DenseVector(size={})",
                    self.vector
                        .as_ref()
                        .unwrap_or(&DenseVector::default())
                        .len()
                ),
            )
            .finish()
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct UpdateVectors {
    /// Points with named vectors
    pub points: Vec<PointStruct>,
}
