use crate::dtos::{
    SessionDataDto, ProviderCacheDto, CompanyProfileDto, 
    TimeSeriesFinancialBatchDto, TimeSeriesFinancialDto, ProviderStatusDto,
    NewWorkflowHistory, WorkflowHistoryDto, WorkflowHistorySummaryDto
};
use crate::config_models::{
    DataSourcesConfig, LlmProvidersConfig,
    AnalysisTemplateSet, AnalysisTemplateSummary
};
use reqwest::{Client, StatusCode};
use uuid::Uuid;
use anyhow::Result;

#[derive(Clone)]
pub struct PersistenceClient {
    client: Client,
    base_url: String,
}

impl PersistenceClient {
    pub fn new(base_url: String) -> Self {
        Self {
            client: Client::new(),
            base_url: base_url.trim_end_matches('/').to_string(),
        }
    }

    // --- Workflow History (NEW) ---

    pub async fn create_workflow_history(&self, dto: &NewWorkflowHistory) -> Result<WorkflowHistoryDto> {
        let url = format!("{}/api/v1/history", self.base_url);
        let resp = self.client
            .post(&url)
            .json(dto)
            .send()
            .await?
            .error_for_status()?;
        let result = resp.json().await?;
        Ok(result)
    }

    pub async fn get_workflow_histories(&self, symbol: Option<&str>, limit: Option<i64>) -> Result<Vec<WorkflowHistorySummaryDto>> {
        let url = format!("{}/api/v1/history", self.base_url);
        let mut req = self.client.get(&url);
        if let Some(s) = symbol {
            req = req.query(&[("symbol", s)]);
        }
        if let Some(l) = limit {
            req = req.query(&[("limit", l)]);
        }
        let resp = req.send().await?.error_for_status()?;
        let results = resp.json().await?;
        Ok(results)
    }

    pub async fn get_workflow_history_by_id(&self, request_id: Uuid) -> Result<WorkflowHistoryDto> {
        let url = format!("{}/api/v1/history/{}", self.base_url, request_id);
        let resp = self.client.get(&url).send().await?.error_for_status()?;
        let result = resp.json().await?;
        Ok(result)
    }

    // --- Session Data ---

    pub async fn insert_session_data(&self, dto: &SessionDataDto) -> Result<()> {
        let url = format!("{}/api/v1/session-data", self.base_url);
        self.client
            .post(&url)
            .json(dto)
            .send()
            .await?
            .error_for_status()?;
        Ok(())
    }

    pub async fn get_session_data(&self, request_id: Uuid) -> Result<Vec<SessionDataDto>> {
        let url = format!("{}/api/v1/session-data/{}", self.base_url, request_id);
        let resp = self.client.get(&url).send().await?.error_for_status()?;
        let data = resp.json().await?;
        Ok(data)
    }

    // --- Provider Cache ---

    pub async fn get_cache(&self, key: &str) -> Result<Option<ProviderCacheDto>> {
        let url = format!("{}/api/v1/provider-cache", self.base_url);
        let resp = self.client
            .get(&url)
            .query(&[("key", key)])
            .send()
            .await?;
        
        if resp.status() == StatusCode::NOT_FOUND {
            return Ok(None);
        }

        let resp = resp.error_for_status()?;
        let data = resp.json().await?;
        Ok(data)
    }

    pub async fn set_cache(&self, dto: &ProviderCacheDto) -> Result<()> {
        let url = format!("{}/api/v1/provider-cache", self.base_url);
        self.client
            .post(&url)
            .json(dto)
            .send()
            .await?
            .error_for_status()?;
        Ok(())
    }

    // --- Existing Methods (Ported for completeness) ---

    pub async fn get_financials(&self, symbol: &str) -> Result<Vec<TimeSeriesFinancialDto>> {
        let url = format!("{}/api/v1/market-data/financial-statements/{}", self.base_url, symbol);
        let resp = self.client.get(&url).send().await?.error_for_status()?;
        let financials = resp.json().await?;
        Ok(financials)
    }

    pub async fn clear_history(&self) -> Result<()> {
        let url = format!("{}/api/v1/system/history", self.base_url);
        self.client.delete(&url).send().await?.error_for_status()?;
        Ok(())
    }

    pub async fn get_company_profile(&self, symbol: &str) -> Result<Option<CompanyProfileDto>> {
        let url = format!("{}/api/v1/companies/{}", self.base_url, symbol);
        let resp = self.client.get(&url).send().await?;
        if resp.status() == StatusCode::NOT_FOUND {
            return Ok(None);
        }
        let profile = resp.error_for_status()?.json().await?;
        Ok(Some(profile))
    }

    pub async fn batch_insert_financials(&self, dtos: Vec<TimeSeriesFinancialDto>) -> Result<()> {
        if dtos.is_empty() {
            return Ok(());
        }
        let url = format!("{}/api/v1/market-data/financials/batch", self.base_url);
        let batch = TimeSeriesFinancialBatchDto { records: dtos };

        self.client
            .post(&url)
            .json(&batch)
            .send()
            .await?
            .error_for_status()?;
        Ok(())
    }

    // --- Configs ---

    pub async fn get_data_sources_config(&self) -> Result<DataSourcesConfig> {
        let url = format!("{}/api/v1/configs/data_sources", self.base_url);
        let resp = self.client.get(&url).send().await?.error_for_status()?;
        let config = resp.json().await?;
        Ok(config)
    }

    pub async fn update_data_sources_config(&self, config: &DataSourcesConfig) -> Result<DataSourcesConfig> {
        let url = format!("{}/api/v1/configs/data_sources", self.base_url);
        let resp = self.client.put(&url).json(config).send().await?.error_for_status()?;
        let updated = resp.json().await?;
        Ok(updated)
    }

    pub async fn get_llm_providers_config(&self) -> Result<LlmProvidersConfig> {
        let url = format!("{}/api/v1/configs/llm_providers", self.base_url);
        let resp = self.client.get(&url).send().await?.error_for_status()?;
        let config = resp.json().await?;
        Ok(config)
    }

    pub async fn update_llm_providers_config(&self, config: &LlmProvidersConfig) -> Result<LlmProvidersConfig> {
        let url = format!("{}/api/v1/configs/llm_providers", self.base_url);
        let resp = self.client.put(&url).json(config).send().await?.error_for_status()?;
        let updated = resp.json().await?;
        Ok(updated)
    }

    // pub async fn get_analysis_template_sets(&self) -> Result<AnalysisTemplateSets> {
    //     let url = format!("{}/api/v1/configs/analysis_template_sets", self.base_url);
    //     let resp = self.client.get(&url).send().await?.error_for_status()?;
    //     let config = resp.json().await?;
    //     Ok(config)
    // }

    // pub async fn update_analysis_template_sets(&self, config: &AnalysisTemplateSets) -> Result<AnalysisTemplateSets> {
    //     let url = format!("{}/api/v1/configs/analysis_template_sets", self.base_url);
    //     let resp = self.client.put(&url).json(config).send().await?.error_for_status()?;
    //     let updated = resp.json().await?;
    //     Ok(updated)
    // }

    // --- Templates (Granular API) ---

    pub async fn get_templates(&self) -> Result<Vec<AnalysisTemplateSummary>> {
        let url = format!("{}/api/v1/templates", self.base_url);
        let resp = self.client.get(&url).send().await?.error_for_status()?;
        let summaries = resp.json().await?;
        Ok(summaries)
    }

    pub async fn get_template_by_id(&self, id: &str) -> Result<AnalysisTemplateSet> {
        let url = format!("{}/api/v1/templates/{}", self.base_url, id);
        let resp = self.client.get(&url).send().await?.error_for_status()?;
        let template = resp.json().await?;
        Ok(template)
    }

    pub async fn update_template(&self, id: &str, template: &AnalysisTemplateSet) -> Result<AnalysisTemplateSet> {
        let url = format!("{}/api/v1/templates/{}", self.base_url, id);
        let resp = self.client.put(&url).json(template).send().await?.error_for_status()?;
        let updated = resp.json().await?;
        Ok(updated)
    }

    pub async fn delete_template(&self, id: &str) -> Result<()> {
        let url = format!("{}/api/v1/templates/{}", self.base_url, id);
        self.client.delete(&url).send().await?.error_for_status()?;
        Ok(())
    }

    // --- Deprecated/Legacy Support ---
    
    pub async fn update_provider_status(&self, symbol: &str, provider_id: &str, status: ProviderStatusDto) -> Result<()> {
         let url = format!("{}/api/v1/companies/{}/providers/{}/status", self.base_url, symbol, provider_id);
         self.client.put(&url).json(&status).send().await?.error_for_status()?;
         Ok(())
    }
    
    pub async fn upsert_company_profile(&self, _profile: CompanyProfileDto) -> Result<()> {
        // Deprecated: No-op
        Ok(())
    }
}
