//! Shared URL construction and response validation for SA API endpoints.

use serde_json::Value;
use tail_fin_common::TailFinError;

use crate::parsing::{parse_article_content, parse_articles, parse_financials, parse_quote};
use crate::types::{Article, ArticleContent, FinancialStatement, Period, Quote, StatementType};

pub const QUOTE_FIELDS: &str = "primary_price,close,price_high_52w,price_low_52w,marketcap,pe_ratio,diluted_eps,today_return,avg_daily_share_volume_1m";

/// Build the API path for a financials request.
pub fn financials_path(ticker: &str, stmt: StatementType, period: Period) -> String {
    format!(
        "/api/v3/symbols/{}/fundamentals_metrics?period_type={}&statement_type={}&target_currency=USD",
        ticker.to_uppercase(),
        period.as_str(),
        stmt.as_str(),
    )
}

/// Parse a financials response, validating the expected array shape.
pub fn parse_financials_response(
    raw: &Value,
    ticker: &str,
    stmt: StatementType,
    period: Period,
) -> Result<FinancialStatement, TailFinError> {
    if raw.as_array().is_none() {
        return Err(TailFinError::Api(format!(
            "SA financials response was not an array for {}",
            ticker
        )));
    }
    Ok(parse_financials(
        raw,
        ticker,
        stmt.as_str(),
        period.as_str(),
    ))
}

/// Build the API path for an article list request (news or analysis).
pub fn articles_path(
    content_type: &str,
    ticker: &str,
    count: usize,
    since: Option<i64>,
    until: Option<i64>,
) -> String {
    let base = format!("/api/v3/symbols/{}/{}", ticker.to_uppercase(), content_type);
    let mut params = format!("page[size]={}&include=author", count);
    if let Some(ts) = since {
        params.push_str(&format!("&filter[since]={}", ts));
    }
    if let Some(ts) = until {
        params.push_str(&format!("&filter[until]={}", ts));
    }
    format!("{}?{}", base, params)
}

/// Parse an articles list response.
pub fn parse_articles_response(raw: &Value, content_type: &str) -> Vec<Article> {
    parse_articles(raw, content_type)
}

/// Build the API path for a quote request.
pub fn quote_path(ticker: &str) -> String {
    format!(
        "/api/v3/symbols/{}/metrics?filter[fields]={}",
        ticker.to_uppercase(),
        QUOTE_FIELDS,
    )
}

/// Parse a quote response, validating the expected data array.
pub fn parse_quote_response(raw: &Value, ticker: &str) -> Result<Quote, TailFinError> {
    if raw.get("data").and_then(|v| v.as_array()).is_none() {
        return Err(TailFinError::Api(format!(
            "SA metrics response missing 'data' array for {}",
            ticker
        )));
    }
    Ok(parse_quote(raw, ticker))
}

/// Build the API path for a single article request.
pub fn article_path(id: &str) -> String {
    format!("/api/v3/articles/{}?include=author", id)
}

/// Parse a single article response.
pub fn parse_article_response(raw: &Value, id: &str) -> Result<ArticleContent, TailFinError> {
    parse_article_content(raw)
        .ok_or_else(|| TailFinError::Parse(format!("SA: failed to parse article {}", id)))
}
