use std::path::Path;

use tail_fin_common::cookies::{load_netscape_file, write_netscape_file};
use tail_fin_common::TailFinError;

use crate::types::{Article, ArticleContent, FinancialStatement, Period, Quote, StatementType};
use crate::{paths, SeekingAlphaApi};

/// Chrome User-Agent used for all HTTP requests.
///
/// SA's Cloudflare config checks the UA string; using a realistic Chrome UA
/// together with the exported session cookies (including `cf_clearance`) allows
/// the API calls to succeed without an active browser session.
const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";

/// HTTP-based SeekingAlpha client that uses saved cookies (no browser needed).
///
/// Create with [`SeekingAlphaHttpClient::from_cookie_file`] after exporting cookies
/// from Chrome via [`export_cookies`].
pub struct SeekingAlphaHttpClient {
    client: reqwest::Client,
    cookie_header: String,
}

impl SeekingAlphaHttpClient {
    /// Load cookies from a Netscape cookie file and build an HTTP client.
    pub fn from_cookie_file(path: &Path) -> Result<Self, TailFinError> {
        if !path.exists() {
            return Err(TailFinError::Api(format!(
                "Cookie file not found: {}. Run `tail-fin sa export-cookies` to export cookies from Chrome.",
                path.display()
            )));
        }

        let cookies = load_netscape_file(path)?;
        let cookie_header = tail_fin_common::cookies::build_cookie_header(&cookies);

        let client = reqwest::Client::builder()
            .user_agent(USER_AGENT)
            .build()
            .map_err(|e| TailFinError::Api(format!("Failed to build HTTP client: {}", e)))?;

        Ok(Self {
            client,
            cookie_header,
        })
    }

    /// Make a GET request to a SA API path and return the parsed JSON.
    async fn api_get(&self, path: &str) -> Result<serde_json::Value, TailFinError> {
        let url = format!("https://seekingalpha.com{}", path);
        let resp = self
            .client
            .get(&url)
            .header("Cookie", &self.cookie_header)
            .header("Accept", "application/json")
            .header("Referer", "https://seekingalpha.com/")
            .send()
            .await
            .map_err(|e| TailFinError::Api(format!("SA request failed: {}", e)))?;

        let status = resp.status();
        if !status.is_success() {
            return Err(TailFinError::Api(format!("SA HTTP {}", status)));
        }

        resp.json::<serde_json::Value>()
            .await
            .map_err(|e| TailFinError::Parse(format!("SA response parse failed: {}", e)))
    }
}

impl SeekingAlphaApi for SeekingAlphaHttpClient {
    async fn income_statement(
        &self,
        ticker: &str,
        period: Period,
    ) -> Result<FinancialStatement, TailFinError> {
        let raw = self
            .api_get(&paths::financials_path(
                ticker,
                StatementType::IncomeStatement,
                period,
            ))
            .await?;
        paths::parse_financials_response(&raw, ticker, StatementType::IncomeStatement, period)
    }

    async fn balance_sheet(
        &self,
        ticker: &str,
        period: Period,
    ) -> Result<FinancialStatement, TailFinError> {
        let raw = self
            .api_get(&paths::financials_path(
                ticker,
                StatementType::BalanceSheet,
                period,
            ))
            .await?;
        paths::parse_financials_response(&raw, ticker, StatementType::BalanceSheet, period)
    }

    async fn cash_flow(
        &self,
        ticker: &str,
        period: Period,
    ) -> Result<FinancialStatement, TailFinError> {
        let raw = self
            .api_get(&paths::financials_path(
                ticker,
                StatementType::CashFlow,
                period,
            ))
            .await?;
        paths::parse_financials_response(&raw, ticker, StatementType::CashFlow, period)
    }

    async fn quote(&self, ticker: &str) -> Result<Quote, TailFinError> {
        let raw = self.api_get(&paths::quote_path(ticker)).await?;
        paths::parse_quote_response(&raw, ticker)
    }

    async fn news(
        &self,
        ticker: &str,
        count: usize,
        since: Option<i64>,
        until: Option<i64>,
    ) -> Result<Vec<Article>, TailFinError> {
        let raw = self
            .api_get(&paths::articles_path("news", ticker, count, since, until))
            .await?;
        Ok(paths::parse_articles_response(&raw, "news"))
    }

    async fn analysis(
        &self,
        ticker: &str,
        count: usize,
        since: Option<i64>,
        until: Option<i64>,
    ) -> Result<Vec<Article>, TailFinError> {
        let raw = self
            .api_get(&paths::articles_path(
                "analysis", ticker, count, since, until,
            ))
            .await?;
        Ok(paths::parse_articles_response(&raw, "analysis"))
    }

    async fn article(&self, id: &str) -> Result<ArticleContent, TailFinError> {
        let raw = self.api_get(&paths::article_path(id)).await?;
        paths::parse_article_response(&raw, id)
    }
}

/// Export SeekingAlpha cookies from a running Chrome instance to a Netscape file.
///
/// Connects to Chrome at `chrome_host` (e.g. `"127.0.0.1:9222"`), grabs all
/// cookies for `seekingalpha.com` (including `cf_clearance`), and writes them
/// to `path` in Netscape format.
pub async fn export_cookies(chrome_host: &str, path: &Path) -> Result<usize, TailFinError> {
    use tail_fin_common::cdp::{find_tab_ws_url, CdpTab};

    let ws_url = find_tab_ws_url(chrome_host, Some("seekingalpha.com")).await?;
    let mut tab = CdpTab::connect(&ws_url).await?;
    let all_cookies = tab.get_all_cookies().await?;

    let sa_cookies: Vec<serde_json::Value> = all_cookies
        .into_iter()
        .filter(|c| {
            c.get("domain")
                .and_then(|v| v.as_str())
                .map(|d| d.contains("seekingalpha.com"))
                .unwrap_or(false)
        })
        .collect();

    if sa_cookies.is_empty() {
        return Err(TailFinError::Api(
            "No SeekingAlpha cookies found. Please log in to seekingalpha.com in Chrome first."
                .into(),
        ));
    }

    write_netscape_file(path, &sa_cookies)?;
    Ok(sa_cookies.len())
}
