use serde::Deserialize;

use crate::{
    error::AppError,
    fh_client::FinnhubClient,
    mapping::{map_financial_dtos, map_profile_dto},
};
use common_contracts::dtos::{CompanyProfileDto, TimeSeriesFinancialDto};
use common_contracts::provider::DataProvider;
use tokio;

#[derive(Debug, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
pub struct FinnhubProfile {
    pub country: Option<String>,
    pub currency: Option<String>,
    pub exchange: Option<String>,
    pub name: Option<String>,
    pub ticker: Option<String>,
    pub ipo: Option<String>,
    pub market_capitalization: Option<f64>,
    pub share_outstanding: Option<f64>,
    pub logo: Option<String>,
    pub phone: Option<String>,
    pub weburl: Option<String>,
    pub finnhub_industry: Option<String>,
}

#[derive(Debug, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
pub struct FinnhubFinancialsReported {
    pub data: Vec<AnnualReport>,
    pub symbol: String,
}

#[derive(Debug, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
pub struct AnnualReport {
    pub year: u16,
    pub start_date: String,
    pub end_date: String,
    pub report: Report,
}

#[derive(Debug, Deserialize, Clone)]
pub struct Report {
    pub bs: Vec<ReportItem>,
    pub ic: Vec<ReportItem>,
    pub cf: Vec<ReportItem>,
}

#[derive(Debug, Deserialize, Clone)]
pub struct ReportItem {
    pub value: f64,
    pub concept: String,
    pub label: String,
}

#[derive(Clone)]
pub struct FinnhubDataProvider {
    client: FinnhubClient,
}

impl DataProvider for FinnhubDataProvider {
    fn name(&self) -> &str {
        "finnhub"
    }

    fn is_enabled(&self) -> bool {
        true
    }
}

impl FinnhubDataProvider {
    pub fn new(api_url: String, api_token: String) -> Self {
        Self {
            client: FinnhubClient::new(api_url, api_token).expect("Failed to create Finnhub client"),
        }
    }

    pub async fn fetch_all_data(
        &self,
        symbol: &str,
    ) -> Result<(CompanyProfileDto, Vec<TimeSeriesFinancialDto>), AppError> {
        let (profile_raw, financials_raw) = self.fetch_raw_data(symbol).await?;

        // 1. Build CompanyProfileDto
        let profile = map_profile_dto(&profile_raw, symbol)?;

        // 2. Build TimeSeriesFinancialDto list
        let financials = map_financial_dtos(&financials_raw, symbol)?;

        Ok((profile, financials))
    }

    async fn fetch_raw_data(
        &self,
        symbol: &str,
    ) -> Result<(FinnhubProfile, FinnhubFinancialsReported), AppError> {
        let params_profile = vec![("symbol".to_string(), symbol.to_string())];
        let params_financials = vec![
            ("symbol".to_string(), symbol.to_string()),
            ("freq".to_string(), "annual".to_string()),
        ];
        let profile_task = self.client.get::<FinnhubProfile>("stock/profile2", params_profile);
        let financials_task =
            self.client
                .get::<FinnhubFinancialsReported>("stock/financials-reported", params_financials);

        let (profile_res, financials_res) = tokio::try_join!(profile_task, financials_task)?;

        Ok((profile_res, financials_res))
    }
}


