//! Database client implementation

use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE};
use serde::{de::DeserializeOwned, Serialize};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;

use super::error::{DatabaseError, Result};
use super::types::*;

/// Synor Database Client
pub struct Client {
    config: Config,
    http_client: reqwest::Client,
    closed: Arc<AtomicBool>,
}

impl Client {
    /// Create a new database client
    pub fn new(config: Config) -> Self {
        let mut headers = HeaderMap::new();
        headers.insert(
            AUTHORIZATION,
            HeaderValue::from_str(&format!("Bearer {}", config.api_key)).unwrap(),
        );
        headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
        headers.insert("X-SDK-Version", HeaderValue::from_static("rust/0.1.0"));

        let http_client = reqwest::Client::builder()
            .default_headers(headers)
            .timeout(Duration::from_secs(config.timeout_secs))
            .build()
            .expect("Failed to create HTTP client");

        Self {
            config,
            http_client,
            closed: Arc::new(AtomicBool::new(false)),
        }
    }

    /// Get the key-value store
    pub fn kv(&self) -> KeyValueStore<'_> {
        KeyValueStore { client: self }
    }

    /// Get the document store
    pub fn documents(&self) -> DocumentStore<'_> {
        DocumentStore { client: self }
    }

    /// Get the vector store
    pub fn vectors(&self) -> VectorStore<'_> {
        VectorStore { client: self }
    }

    /// Get the time series store
    pub fn timeseries(&self) -> TimeSeriesStore<'_> {
        TimeSeriesStore { client: self }
    }

    /// Get database statistics
    pub async fn get_stats(&self) -> Result<DatabaseStats> {
        self.request("GET", "/stats", Option::<()>::None).await
    }

    /// Health check
    pub async fn health_check(&self) -> bool {
        #[derive(serde::Deserialize)]
        struct HealthResponse {
            status: String,
        }

        match self.request::<_, HealthResponse>("GET", "/health", Option::<()>::None).await {
            Ok(resp) => resp.status == "healthy",
            Err(_) => false,
        }
    }

    /// Close the client
    pub fn close(&self) {
        self.closed.store(true, Ordering::SeqCst);
    }

    /// Check if client is closed
    pub fn is_closed(&self) -> bool {
        self.closed.load(Ordering::SeqCst)
    }

    async fn request<B: Serialize, R: DeserializeOwned>(
        &self,
        method: &str,
        path: &str,
        body: Option<B>,
    ) -> Result<R> {
        if self.is_closed() {
            return Err(DatabaseError::new("Client has been closed"));
        }

        let mut last_error = None;

        for attempt in 0..self.config.retries {
            match self.do_request(method, path, &body).await {
                Ok(result) => return Ok(result),
                Err(e) => {
                    if self.config.debug {
                        eprintln!("Attempt {} failed: {}", attempt + 1, e);
                    }
                    last_error = Some(e);
                    if attempt < self.config.retries - 1 {
                        tokio::time::sleep(Duration::from_secs((attempt + 1) as u64)).await;
                    }
                }
            }
        }

        Err(last_error.unwrap_or_else(|| DatabaseError::new("Unknown error after retries")))
    }

    async fn do_request<B: Serialize, R: DeserializeOwned>(
        &self,
        method: &str,
        path: &str,
        body: &Option<B>,
    ) -> Result<R> {
        let url = format!("{}{}", self.config.endpoint, path);

        let mut request = match method {
            "GET" => self.http_client.get(&url),
            "POST" => self.http_client.post(&url),
            "PUT" => self.http_client.put(&url),
            "PATCH" => self.http_client.patch(&url),
            "DELETE" => self.http_client.delete(&url),
            _ => return Err(DatabaseError::new(format!("Unknown method: {}", method))),
        };

        if let Some(b) = body {
            request = request.json(b);
        }

        let response = request.send().await?;
        let status = response.status();

        if !status.is_success() {
            let error_body: serde_json::Value = response.json().await.unwrap_or_default();
            let message = error_body["message"]
                .as_str()
                .or_else(|| error_body["error"].as_str())
                .unwrap_or(&format!("HTTP {}", status.as_u16()));

            return Err(DatabaseError::new(message)
                .with_status(status.as_u16())
                .with_code(error_body["code"].as_str().unwrap_or("").to_string()));
        }

        let result = response.json().await?;
        Ok(result)
    }
}

/// Key-Value store operations
pub struct KeyValueStore<'a> {
    client: &'a Client,
}

impl<'a> KeyValueStore<'a> {
    /// Get a value by key
    pub async fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>> {
        #[derive(serde::Deserialize)]
        struct Response<T> {
            value: Option<T>,
        }

        let path = format!("/kv/{}", urlencoding::encode(key));
        let resp: Response<T> = self.client.request("GET", &path, Option::<()>::None).await?;
        Ok(resp.value)
    }

    /// Get a full entry with metadata
    pub async fn get_entry<T: DeserializeOwned>(&self, key: &str) -> Result<Option<KeyValueEntry<T>>> {
        let path = format!("/kv/{}/entry", urlencoding::encode(key));
        self.client.request("GET", &path, Option::<()>::None).await
    }

    /// Set a value
    pub async fn set<T: Serialize>(&self, key: &str, value: T, options: Option<SetOptions>) -> Result<()> {
        #[derive(Serialize)]
        struct SetRequest<T> {
            value: T,
            #[serde(flatten)]
            options: Option<SetOptions>,
        }

        let path = format!("/kv/{}", urlencoding::encode(key));
        let body = SetRequest { value, options };
        self.client.request::<_, serde_json::Value>("PUT", &path, Some(body)).await?;
        Ok(())
    }

    /// Delete a key
    pub async fn delete(&self, key: &str) -> Result<bool> {
        #[derive(serde::Deserialize)]
        struct Response {
            deleted: bool,
        }

        let path = format!("/kv/{}", urlencoding::encode(key));
        let resp: Response = self.client.request("DELETE", &path, Option::<()>::None).await?;
        Ok(resp.deleted)
    }

    /// Check if a key exists
    pub async fn exists(&self, key: &str) -> Result<bool> {
        #[derive(serde::Deserialize)]
        struct Response {
            exists: bool,
        }

        let path = format!("/kv/{}/exists", urlencoding::encode(key));
        let resp: Response = self.client.request("GET", &path, Option::<()>::None).await?;
        Ok(resp.exists)
    }

    /// List keys
    pub async fn list<T: DeserializeOwned>(&self, options: Option<ListOptions>) -> Result<ListResult<T>> {
        let mut path = "/kv".to_string();
        if let Some(opts) = &options {
            let mut params = vec![];
            if let Some(prefix) = &opts.prefix {
                params.push(format!("prefix={}", urlencoding::encode(prefix)));
            }
            if let Some(cursor) = &opts.cursor {
                params.push(format!("cursor={}", urlencoding::encode(cursor)));
            }
            if let Some(limit) = opts.limit {
                params.push(format!("limit={}", limit));
            }
            if !params.is_empty() {
                path = format!("{}?{}", path, params.join("&"));
            }
        }

        self.client.request("GET", &path, Option::<()>::None).await
    }

    /// Increment a numeric value
    pub async fn incr(&self, key: &str, by: i64) -> Result<i64> {
        #[derive(Serialize)]
        struct IncrRequest {
            by: i64,
        }

        #[derive(serde::Deserialize)]
        struct Response {
            value: i64,
        }

        let path = format!("/kv/{}/incr", urlencoding::encode(key));
        let resp: Response = self.client.request("POST", &path, Some(IncrRequest { by })).await?;
        Ok(resp.value)
    }
}

/// Document store operations
pub struct DocumentStore<'a> {
    client: &'a Client,
}

impl<'a> DocumentStore<'a> {
    /// Create a new document
    pub async fn create<T: Serialize + DeserializeOwned>(
        &self,
        collection: &str,
        data: T,
        options: Option<CreateDocumentOptions>,
    ) -> Result<Document<T>> {
        #[derive(Serialize)]
        struct CreateRequest<T> {
            data: T,
            #[serde(flatten)]
            options: Option<CreateDocumentOptions>,
        }

        let path = format!("/documents/{}", urlencoding::encode(collection));
        let body = CreateRequest { data, options };
        self.client.request("POST", &path, Some(body)).await
    }

    /// Get a document by ID
    pub async fn get<T: DeserializeOwned>(&self, collection: &str, id: &str) -> Result<Option<Document<T>>> {
        let path = format!("/documents/{}/{}", urlencoding::encode(collection), urlencoding::encode(id));
        self.client.request("GET", &path, Option::<()>::None).await
    }

    /// Update a document
    pub async fn update<T: Serialize + DeserializeOwned>(
        &self,
        collection: &str,
        id: &str,
        update: T,
        options: Option<UpdateDocumentOptions>,
    ) -> Result<Document<T>> {
        #[derive(Serialize)]
        struct UpdateRequest<T> {
            update: T,
            #[serde(flatten)]
            options: Option<UpdateDocumentOptions>,
        }

        let path = format!("/documents/{}/{}", urlencoding::encode(collection), urlencoding::encode(id));
        let body = UpdateRequest { update, options };
        self.client.request("PATCH", &path, Some(body)).await
    }

    /// Delete a document
    pub async fn delete(&self, collection: &str, id: &str) -> Result<bool> {
        #[derive(serde::Deserialize)]
        struct Response {
            deleted: bool,
        }

        let path = format!("/documents/{}/{}", urlencoding::encode(collection), urlencoding::encode(id));
        let resp: Response = self.client.request("DELETE", &path, Option::<()>::None).await?;
        Ok(resp.deleted)
    }

    /// Query documents
    pub async fn query<T: DeserializeOwned>(&self, collection: &str, options: Option<QueryOptions>) -> Result<QueryResult<T>> {
        let path = format!("/documents/{}/query", urlencoding::encode(collection));
        self.client.request("POST", &path, options.or(Some(QueryOptions::default()))).await
    }

    /// Count documents
    pub async fn count(&self, collection: &str, filter: Option<serde_json::Value>) -> Result<u64> {
        #[derive(Serialize)]
        struct CountRequest {
            filter: Option<serde_json::Value>,
        }

        #[derive(serde::Deserialize)]
        struct Response {
            count: u64,
        }

        let path = format!("/documents/{}/count", urlencoding::encode(collection));
        let resp: Response = self.client.request("POST", &path, Some(CountRequest { filter })).await?;
        Ok(resp.count)
    }
}

/// Vector store operations
pub struct VectorStore<'a> {
    client: &'a Client,
}

impl<'a> VectorStore<'a> {
    /// Create a vector collection
    pub async fn create_collection(&self, config: VectorCollectionConfig) -> Result<()> {
        self.client.request::<_, serde_json::Value>("POST", "/vectors/collections", Some(config)).await?;
        Ok(())
    }

    /// Delete a vector collection
    pub async fn delete_collection(&self, name: &str) -> Result<()> {
        let path = format!("/vectors/collections/{}", urlencoding::encode(name));
        self.client.request::<_, serde_json::Value>("DELETE", &path, Option::<()>::None).await?;
        Ok(())
    }

    /// Upsert vectors
    pub async fn upsert(
        &self,
        collection: &str,
        vectors: Vec<VectorEntry>,
        options: Option<UpsertVectorOptions>,
    ) -> Result<u64> {
        #[derive(Serialize)]
        struct UpsertRequest {
            vectors: Vec<VectorEntry>,
            #[serde(flatten)]
            options: Option<UpsertVectorOptions>,
        }

        #[derive(serde::Deserialize)]
        struct Response {
            upserted: u64,
        }

        let path = format!("/vectors/{}/upsert", urlencoding::encode(collection));
        let resp: Response = self.client.request("POST", &path, Some(UpsertRequest { vectors, options })).await?;
        Ok(resp.upserted)
    }

    /// Search for similar vectors
    pub async fn search(
        &self,
        collection: &str,
        vector: Vec<f64>,
        options: Option<SearchOptions>,
    ) -> Result<Vec<SearchResult>> {
        #[derive(Serialize)]
        struct SearchRequest {
            vector: Vec<f64>,
            #[serde(flatten)]
            options: Option<SearchOptions>,
        }

        #[derive(serde::Deserialize)]
        struct Response {
            results: Vec<SearchResult>,
        }

        let path = format!("/vectors/{}/search", urlencoding::encode(collection));
        let resp: Response = self.client.request("POST", &path, Some(SearchRequest { vector, options })).await?;
        Ok(resp.results)
    }

    /// Delete vectors by ID
    pub async fn delete(&self, collection: &str, ids: Vec<String>, namespace: Option<String>) -> Result<u64> {
        #[derive(Serialize)]
        struct DeleteRequest {
            ids: Vec<String>,
            #[serde(skip_serializing_if = "Option::is_none")]
            namespace: Option<String>,
        }

        #[derive(serde::Deserialize)]
        struct Response {
            deleted: u64,
        }

        let path = format!("/vectors/{}/delete", urlencoding::encode(collection));
        let resp: Response = self.client.request("POST", &path, Some(DeleteRequest { ids, namespace })).await?;
        Ok(resp.deleted)
    }
}

/// Time series store operations
pub struct TimeSeriesStore<'a> {
    client: &'a Client,
}

impl<'a> TimeSeriesStore<'a> {
    /// Write data points
    pub async fn write(
        &self,
        series: &str,
        points: Vec<DataPoint>,
        options: Option<WritePointsOptions>,
    ) -> Result<u64> {
        #[derive(Serialize)]
        struct WriteRequest {
            points: Vec<DataPoint>,
            #[serde(flatten)]
            options: Option<WritePointsOptions>,
        }

        #[derive(serde::Deserialize)]
        struct Response {
            written: u64,
        }

        let path = format!("/timeseries/{}/write", urlencoding::encode(series));
        let resp: Response = self.client.request("POST", &path, Some(WriteRequest { points, options })).await?;
        Ok(resp.written)
    }

    /// Query a time series
    pub async fn query(
        &self,
        series: &str,
        range: TimeRange,
        options: Option<TimeSeriesQueryOptions>,
    ) -> Result<TimeSeriesResult> {
        #[derive(Serialize)]
        struct QueryRequest {
            range: TimeRange,
            #[serde(flatten)]
            options: Option<TimeSeriesQueryOptions>,
        }

        let path = format!("/timeseries/{}/query", urlencoding::encode(series));
        self.client.request("POST", &path, Some(QueryRequest { range, options })).await
    }

    /// Delete data points
    pub async fn delete(
        &self,
        series: &str,
        range: TimeRange,
        tags: Option<std::collections::HashMap<String, String>>,
    ) -> Result<u64> {
        #[derive(Serialize)]
        struct DeleteRequest {
            range: TimeRange,
            #[serde(skip_serializing_if = "Option::is_none")]
            tags: Option<std::collections::HashMap<String, String>>,
        }

        #[derive(serde::Deserialize)]
        struct Response {
            deleted: u64,
        }

        let path = format!("/timeseries/{}/delete", urlencoding::encode(series));
        let resp: Response = self.client.request("POST", &path, Some(DeleteRequest { range, tags })).await?;
        Ok(resp.deleted)
    }

    /// Get series info
    pub async fn get_series_info(&self, series: &str) -> Result<SeriesInfo> {
        let path = format!("/timeseries/{}/info", urlencoding::encode(series));
        self.client.request("GET", &path, Option::<()>::None).await
    }

    /// List all series
    pub async fn list_series(&self, prefix: Option<&str>) -> Result<Vec<SeriesInfo>> {
        #[derive(serde::Deserialize)]
        struct Response {
            series: Vec<SeriesInfo>,
        }

        let path = match prefix {
            Some(p) => format!("/timeseries?prefix={}", urlencoding::encode(p)),
            None => "/timeseries".to_string(),
        };
        let resp: Response = self.client.request("GET", &path, Option::<()>::None).await?;
        Ok(resp.series)
    }

    /// Set retention policy
    pub async fn set_retention(&self, series: &str, retention_days: i32) -> Result<()> {
        #[derive(Serialize)]
        struct Request {
            retention_days: i32,
        }

        let path = format!("/timeseries/{}/retention", urlencoding::encode(series));
        self.client.request::<_, serde_json::Value>("PUT", &path, Some(Request { retention_days })).await?;
        Ok(())
    }
}
