pub mod index;
pub mod points;
pub mod snapshots;

use crate::error::QdrantError;
use log::warn;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::error::Error;
use std::fmt::{Display, Formatter};
use tokio::time::Instant;

#[derive(Debug, Serialize)]
pub(crate) struct CollectionName {
    name: String,
}

impl CollectionName {
    pub(crate) fn new(name: String) -> Self {
        Self { name }
    }
}

#[derive(Debug, Serialize)]
pub(crate) struct CollectionResponse {
    collections: Vec<CollectionName>,
}

impl CollectionResponse {
    pub(crate) fn new(collections: Vec<CollectionName>) -> Self {
        Self { collections }
    }
}

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

#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub(crate) enum Distance {
    #[serde(alias = "cosine", alias = "COSINE")]
    #[default]
    Cosine,
    #[serde(alias = "euclid", alias = "EUCLID")]
    Euclid,
    #[serde(alias = "dot", alias = "DOT")]
    Dot,
    #[serde(alias = "manhattan", alias = "MANHATTAN")]
    Manhattan,
}

impl Display for Distance {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Distance::Cosine => write!(f, "Cosine"),
            Distance::Euclid => write!(f, "Euclid"),
            Distance::Dot => write!(f, "Dot"),
            Distance::Manhattan => write!(f, "Manhattan"),
        }
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub(crate) struct VectorsConfig {
    pub(crate) size: u64,
    pub(crate) distance: Distance,
}

#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct HnswConfig {
    // Number of edges per node in the index graph. Larger the value - more accurate the search, more space required.
    pub(crate) m: u32,
    // Custom M param for hnsw graph built for payload index. If not set, default M will be used
    #[serde(default)]
    pub(crate) payload_m: u32,
    // Number of neighbours to consider during the index building. Larger the value - more accurate the search, more time required to build index.
    pub(crate) ef_construct: u32,
    // Minimal size (in KiloBytes) of vectors for additional payload-based indexing. If payload chunk is smaller than `full_scan_threshold_kb` additional indexing won't be used - in this case full-scan search should be preferred by query planner and additional indexing is not required. Note: 1Kb = 1 vector of size 256
    pub(crate) full_scan_threshold: u32,
    // Number of parallel threads used for background index building. If 0 - automatically select from 8 to 16. Best to keep between 8 and 16 to prevent likelihood of slow building or broken/inefficient HNSW graphs. On small CPUs, less threads are used.
    #[serde(default)]
    pub(crate) max_indexing_threads: u32,
    // Store HNSW index on disk. If set to false, index will be stored in RAM. Default: false
    #[serde(default)]
    pub(crate) on_disk: bool,
}

impl Default for HnswConfig {
    fn default() -> Self {
        Self {
            m: 16,
            payload_m: 16,
            ef_construct: 200,
            full_scan_threshold: 1024,
            max_indexing_threads: 0,
            on_disk: false,
        }
    }
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct CreateCollection {
    pub(crate) vectors: VectorsConfig,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub(crate) hnsw_config: Option<HnswConfig>,
}

/// Type, used for specifying point ID in user interface
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(untagged)]
pub(crate) enum PointIdType {
    NumId(u64),
    Uuid(String),
}

impl PartialEq for PointIdType {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (PointIdType::NumId(a), PointIdType::NumId(b)) => a == b,
            (PointIdType::Uuid(a), PointIdType::Uuid(b)) => a == b,
            _ => false,
        }
    }
}

impl From<&str> for PointIdType {
    fn from(value: &str) -> Self {
        PointIdType::Uuid(value.to_string())
    }
}

impl Eq for PointIdType {}

impl Display for PointIdType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            PointIdType::NumId(id) => write!(f, "{}", id),
            PointIdType::Uuid(uuid) => write!(f, "{}", uuid),
        }
    }
}

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

/// `Acknowledged` - Request is saved to WAL and will be process in a queue.
/// `Completed` - Request is completed, changes are actual.
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub(crate) enum UpdateStatus {
    Acknowledged,
    Completed,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct UpdateResult {
    /// Sequential number of the operation
    #[serde(skip_serializing_if = "Option::is_none")]
    pub operation_id: Option<u64>,

    /// Update status
    pub status: UpdateStatus,
}

#[derive(Clone, Deserialize, Serialize)]
pub(crate) struct Record {
    /// Id of the point
    pub(crate) id: PointIdType,
    /// Payload - values assigned to the point
    #[serde(skip_serializing_if = "Option::is_none")]
    pub(crate) payload: Option<Payload>,
    /// Vector of the point
    #[serde(skip_serializing_if = "Option::is_none")]
    pub(crate) vector: Option<DenseVector>,
}

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

#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct DummyOptimizerConfig {
    deleted_threshold: f32,
    vacuum_min_vector_number: u32,
    default_segment_number: u32,
    flush_interval_sec: u32,
}

impl Default for DummyOptimizerConfig {
    fn default() -> Self {
        Self {
            deleted_threshold: 1.1,
            vacuum_min_vector_number: 1,
            default_segment_number: 1,
            flush_interval_sec: 1,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct DummyWalConfig {
    wal_capacity_mb: u32,
    wal_segments_ahead: u32,
}

impl Default for DummyWalConfig {
    fn default() -> Self {
        Self {
            wal_capacity_mb: 32,
            wal_segments_ahead: 0,
        }
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub(crate) struct DummyStruct {
    pub(crate) vectors: VectorsConfig,
}

#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct CollectionConfig {
    pub(crate) params: DummyStruct,
    pub(crate) hnsw_config: Option<HnswConfig>,
    pub(crate) optimizer_config: DummyOptimizerConfig,
    pub(crate) wal_config: DummyWalConfig,
}

impl CollectionConfig {
    fn new(hnsw_config: Option<HnswConfig>, vectors: VectorsConfig) -> Self {
        Self {
            hnsw_config: match hnsw_config {
                Some(hnsw_config) => Some(hnsw_config),
                None => Some(HnswConfig::default()),
            },
            params: DummyStruct { vectors },
            optimizer_config: Default::default(),
            wal_config: Default::default(),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct CollectionDetail {
    pub(crate) status: String,
    pub(crate) optimizer_status: String,
    pub(crate) segments_count: usize,
    pub(crate) vectors_count: Option<usize>,
    pub(crate) indexed_vectors_count: usize,
    pub(crate) points_count: u64,
    pub(crate) config: CollectionConfig,
    pub(crate) payload_schema: Payload,
}

#[derive(Debug, Serialize)]
pub(crate) struct CollectionExists {
    exists: bool,
}

impl From<bool> for CollectionExists {
    fn from(value: bool) -> Self {
        Self { exists: value }
    }
}

impl From<CollectionExists> for bool {
    fn from(value: CollectionExists) -> bool {
        value.exists
    }
}

impl CollectionDetail {
    pub(crate) fn new(
        points_count: u64,
        hnsw_config: Option<HnswConfig>,
        vectors: VectorsConfig,
        payload_schema: Option<String>,
    ) -> Self {
        let schema_str = payload_schema.unwrap_or_else(|| "{}".to_string());
        Self {
            status: "green".into(),
            optimizer_status: "ok".into(),
            segments_count: 1,
            vectors_count: Some(0),
            indexed_vectors_count: 1,
            points_count,
            config: CollectionConfig::new(hnsw_config, vectors),
            payload_schema: serde_json::from_str(&schema_str).unwrap_or(Payload::default()),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ApiStatus {
    Ok,
    Error(String),
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct ApiResponse<T> {
    pub(crate) time: f32,
    pub(crate) status: ApiStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub(crate) result: Option<T>,
}

impl<T> ApiResponse<T> {
    pub(crate) fn ok(instant: Instant, t: impl Into<Option<T>>) -> Self {
        Self {
            time: instant.elapsed().as_secs_f32(),
            status: ApiStatus::Ok,
            result: t.into(),
        }
    }

    pub(crate) fn error(instant: Instant, e: impl Error) -> Self {
        ApiResponse {
            time: instant.elapsed().as_secs_f32(),
            status: ApiStatus::Error(e.to_string()),
            result: None,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct UsageResponse<T> {
    pub(crate) time: f32,
    pub(crate) status: ApiStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub(crate) usage: Option<T>,
}

#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Payload(pub(crate) Map<String, Value>);

impl TryFrom<String> for Payload {
    type Error = QdrantError;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        serde_json::from_str(&value).map(Payload).map_err(|e| {
            warn!("Failed to parse payload: {}", e);
            QdrantError::BadRequestWithMsg(e.to_string())
        })
    }
}

impl TryFrom<(String, String)> for Payload {
    type Error = QdrantError;

    fn try_from(value: (String, String)) -> Result<Self, Self::Error> {
        let (key, value) = value;
        let mut map = Map::new();
        map.insert(key, Value::String(value));
        Ok(Payload(map))
    }
}

impl TryFrom<sqlx::types::Json<Value>> for Payload {
    type Error = QdrantError;

    fn try_from(value: sqlx::types::Json<Value>) -> Result<Self, Self::Error> {
        serde_json::from_value(value.0).map(Payload).map_err(|e| {
            warn!("Failed to parse payload: {}", e);
            QdrantError::BadRequestWithMsg(e.to_string())
        })
    }
}

impl TryInto<String> for Payload {
    type Error = QdrantError;

    fn try_into(self) -> Result<String, Self::Error> {
        serde_json::to_string(&self.0)
            .map_err(|e| QdrantError::InternalServerErrorWithMsg(e.to_string()))
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_payload_try_from_string() {
        let json_str = r#"{"key": "value"}"#.to_string();
        let payload = Payload::try_from(json_str).unwrap();
        assert_eq!(
            payload.0.get("key").unwrap(),
            &Value::String("value".to_string())
        );
    }

    #[test]
    fn test_payload_try_from_invalid_string() {
        let json_str = r#"{"key": "value""#.to_string(); // Invalid JSON
        let result = Payload::try_from(json_str);
        assert!(result.is_err());
    }

    #[test]
    fn test_payload_try_from_empty_string() {
        let json_str = "".to_string();
        let payload = Payload::try_from(json_str);
        assert!(payload.is_err());
    }
}
