use std::time::Instant;

use chrono::Utc;

use crate::error::{AxiomError, Result};
use crate::models::{FindResult, RequestLogEntry, RetrievalTrace, SearchOptions};
use crate::uri::AxiomUri;

use super::AxiomSync;

impl AxiomSync {
    pub fn get_trace(&self, trace_id: &str) -> Result<Option<RetrievalTrace>> {
        let Some(entry) = self.state.get_trace_index(trace_id)? else {
            return Ok(None);
        };
        let uri = AxiomUri::parse(&entry.uri)?;
        let raw = self.fs.read(&uri)?;
        let trace = serde_json::from_str::<RetrievalTrace>(&raw)?;
        Ok(Some(trace))
    }

    pub fn replay_trace(&self, trace_id: &str, limit: Option<usize>) -> Result<Option<FindResult>> {
        let request_id = uuid::Uuid::new_v4().to_string();
        let started = Instant::now();

        let output = (|| -> Result<Option<FindResult>> {
            let Some(stored_trace) = self.get_trace(trace_id)? else {
                return Ok(None);
            };

            let target_uri = stored_trace
                .target_uri
                .as_deref()
                .map(AxiomUri::parse)
                .transpose()?;
            let replay_limit = limit.unwrap_or_else(|| stored_trace.final_topk.len().max(1));
            let request_type = format!("{}_replay", stored_trace.request_type);

            let mut result = {
                let index = self
                    .index
                    .read()
                    .map_err(|_| AxiomError::lock_poisoned("index"))?;
                let options = SearchOptions {
                    query: stored_trace.query,
                    target_uri,
                    session: None,
                    session_hints: Vec::new(),
                    budget: None,
                    limit: replay_limit,
                    score_threshold: None,
                    min_match_tokens: None,
                    filter: None,
                    request_type,
                };
                self.drr.run(&index, &options)
            };
            self.persist_trace_result(&mut result)?;
            Ok(Some(result))
        })();

        match output {
            Ok(Some(result)) => {
                let replay_trace_id = result.trace.as_ref().map(|x| x.trace_id.clone());
                self.try_log_request(&RequestLogEntry {
                    request_id,
                    operation: "trace.replay".to_string(),
                    status: "ok".to_string(),
                    latency_ms: started.elapsed().as_millis(),
                    created_at: Utc::now().to_rfc3339(),
                    trace_id: replay_trace_id,
                    target_uri: None,
                    error_code: None,
                    error_message: None,
                    details: Some(serde_json::json!({
                        "source_trace_id": trace_id,
                        "limit": limit,
                    })),
                });
                Ok(Some(result))
            }
            Ok(None) => {
                self.try_log_request(&RequestLogEntry {
                    request_id,
                    operation: "trace.replay".to_string(),
                    status: "not_found".to_string(),
                    latency_ms: started.elapsed().as_millis(),
                    created_at: Utc::now().to_rfc3339(),
                    trace_id: None,
                    target_uri: None,
                    error_code: None,
                    error_message: None,
                    details: Some(serde_json::json!({
                        "source_trace_id": trace_id,
                        "limit": limit,
                    })),
                });
                Ok(None)
            }
            Err(err) => {
                self.try_log_request(&RequestLogEntry {
                    request_id,
                    operation: "trace.replay".to_string(),
                    status: "error".to_string(),
                    latency_ms: started.elapsed().as_millis(),
                    created_at: Utc::now().to_rfc3339(),
                    trace_id: None,
                    target_uri: None,
                    error_code: Some(err.code().to_string()),
                    error_message: Some(err.to_string()),
                    details: Some(serde_json::json!({
                        "source_trace_id": trace_id,
                        "limit": limit,
                    })),
                });
                Err(err)
            }
        }
    }
}
