use super::AIModel;
use crate::bincode::{BinCodeSerAndDeser, BinCodeSerAndDeserResponse};
use crate::client::ConnectedClient;
use crate::db::{ServerInfo, StoreUpsert};
use crate::keyval::StoreInput;
use crate::keyval::StoreName;
use crate::keyval::StoreValue;
use crate::similarity::Similarity;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashSet;
use std::hash::Hash;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum AIServerResponse {
    // Unit variant for no action
    Unit,
    Pong,
    // List of connected clients. Potentially outdated at the point of read
    ClientList(HashSet<ConnectedClient>),
    StoreList(HashSet<AIStoreInfo>),
    InfoServer(ServerInfo),
    Set(StoreUpsert),
    // Always returned in order of the key request, however when GetPred is used, there is no key
    // request so the order can be mixed up
    Get(Vec<(Option<StoreInput>, StoreValue)>),
    // StoreInput can be None if the store was created with `store_original` as false
    GetSimN(Vec<(Option<StoreInput>, StoreValue, Similarity)>),
    // number of deleted entities
    Del(usize),
    // number of created indexes
    CreateIndex(usize),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AIStoreInfo {
    pub name: StoreName,
    pub query_model: AIModel,
    pub index_model: AIModel,
    pub embedding_size: usize,
}
pub type AIServerResultInner = Vec<Result<AIServerResponse, String>>;
// ServerResult: Given that an array of queries are sent in, we expect that an array of responses
// be returned each being a potential error
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AIServerResult {
    results: AIServerResultInner,
}

impl BinCodeSerAndDeser for AIServerResult {}

impl AIServerResult {
    pub fn with_capacity(len: usize) -> Self {
        Self {
            results: Vec::with_capacity(len),
        }
    }

    pub fn pop(mut self) -> Option<Result<AIServerResponse, String>> {
        self.results.pop()
    }

    pub fn push(&mut self, entry: Result<AIServerResponse, String>) {
        self.results.push(entry)
    }
    pub fn len(&self) -> usize {
        self.results.len()
    }

    pub fn is_empty(&self) -> bool {
        self.results.is_empty()
    }

    pub fn into_inner(self) -> AIServerResultInner {
        self.results
    }
}

impl BinCodeSerAndDeserResponse for AIServerResult {
    fn from_error(err: String) -> Self {
        Self {
            results: vec![Err(err)],
        }
    }
}
