use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use crate::news::NewsArticle;

/// Frontend request payload for slash-command execution.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ExecuteTerminalCommandRequest {
    /// Workspace that originated the command.
    pub workspace_id: String,
    /// Raw slash-command input exactly as entered by the user.
    pub input: String,
}

/// Frontend request payload for direct live company lookup by ticker symbol.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LookupCompanyRequest {
    /// Symbol to resolve through the live quote provider.
    pub symbol: String,
}

/// Parsed slash command used internally by the backend command service.
#[derive(Debug, Clone, PartialEq)]
pub struct ChatCommandRequest {
    /// Normalized command verb such as `/search`.
    pub command: String,
    /// Positional command arguments.
    pub args: Vec<String>,
    /// Original raw input for debugging or future auditing.
    pub raw_input: String,
}

/// Backend response envelope for slash-command execution.
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum TerminalCommandResponse {
    /// Plain text response rendered directly in the terminal.
    Text {
        content: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        portfolio: Option<Portfolio>,
    },
    /// Structured payload rendered by an existing terminal panel.
    Panel { panel: Box<PanelPayload> },
}

impl TerminalCommandResponse {
    #[must_use]
    pub fn panel(panel: PanelPayload) -> Self {
        Self::Panel {
            panel: Box::new(panel),
        }
    }
}

/// Serializable panel variants shared between Rust and React.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum PanelPayload {
    Company {
        data: Company,
    },
    Error {
        data: ErrorPanel,
    },
    Portfolio {
        data: Portfolio,
    },
    News {
        data: Vec<NewsArticle>,
        ticker: Option<String>,
    },
    Analysis {
        data: StockAnalysis,
    },
    Financials {
        data: FinancialsPanelData,
    },
    CashFlow {
        data: CashFlowPanelData,
    },
    Dividends {
        data: DividendsPanelData,
    },
    Earnings {
        data: EarningsPanelData,
    },
}

/// Structured error payload rendered as a terminal card.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ErrorPanel {
    pub title: String,
    pub message: String,
    pub detail: Option<String>,
    pub provider: Option<String>,
    pub query: Option<String>,
    pub symbol: Option<String>,
}

/// Company snapshot used by the company panel.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Company {
    pub symbol: String,
    pub name: String,
    pub price: f64,
    pub change: f64,
    pub change_percent: f64,
    pub market_cap: f64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub volume: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub volume_label: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pe: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub eps: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub high52_week: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub low52_week: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub profile: Option<CompanyProfile>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub price_chart: Option<Vec<CompanyPricePoint>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub price_chart_ranges: Option<HashMap<String, Vec<CompanyPricePoint>>>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CompanyProfile {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub wiki_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ceo: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub headquarters: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub employees: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub founded: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sector: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub website: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CompanyPricePoint {
    pub label: String,
    pub price: f64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub volume: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timestamp: Option<String>,
}

/// Portfolio holding row.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Holding {
    pub symbol: String,
    pub name: String,
    pub quantity: f64,
    pub avg_cost: f64,
    pub current_price: f64,
    pub current_value: f64,
    pub gain_loss: f64,
    pub gain_loss_percent: f64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cost_basis: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub unrealized_gain: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub latest_trade_at: Option<String>,
}

/// Portfolio summary and holdings data.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Portfolio {
    pub holdings: Vec<Holding>,
    pub total_value: f64,
    pub day_change: f64,
    pub day_change_percent: f64,
    pub total_gain: f64,
    pub total_gain_percent: f64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cash_balance: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub invested_cost_basis: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub realized_gain: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub unrealized_gain: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub holdings_count: Option<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stale_pricing_symbols: Option<Vec<String>>,
}

/// Structured analysis payload for the analysis panel.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct StockAnalysis {
    pub symbol: String,
    pub summary: String,
    pub sentiment: String,
    pub key_points: Vec<String>,
    pub risks: Vec<String>,
    pub opportunities: Vec<String>,
    pub recommendation: String,
    pub target_price: Option<f64>,
}

/// Shared mock financial fixture loaded by both the frontend and backend.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MockFinancialData {
    #[allow(dead_code)]
    pub companies: Vec<Company>,
    pub analyses: HashMap<String, StockAnalysis>,
}

#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum Frequency {
    Annual,
    Quarterly,
}

impl Frequency {
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Annual => "annual",
            Self::Quarterly => "quarterly",
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct FilingRef {
    pub accession_number: String,
    pub filing_date: String,
    pub report_date: Option<String>,
    pub form: String,
    pub primary_document: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct SourceStatus {
    pub companyfacts_used: bool,
    pub latest_xbrl_parsed: bool,
    pub degraded_reason: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct StatementPeriod {
    pub label: String,
    pub fiscal_year: Option<String>,
    pub fiscal_period: Option<String>,
    pub period_start: Option<String>,
    pub period_end: String,
    pub filed_date: String,
    pub form: String,
    pub revenue: Option<f64>,
    pub gross_profit: Option<f64>,
    pub operating_income: Option<f64>,
    pub net_income: Option<f64>,
    pub diluted_eps: Option<f64>,
    pub cash_and_equivalents: Option<f64>,
    pub total_assets: Option<f64>,
    pub total_liabilities: Option<f64>,
    pub total_equity: Option<f64>,
    pub shares_outstanding: Option<f64>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CashFlowPeriod {
    pub label: String,
    pub fiscal_year: Option<String>,
    pub fiscal_period: Option<String>,
    pub period_start: Option<String>,
    pub period_end: String,
    pub filed_date: String,
    pub form: String,
    pub operating_cash_flow: Option<f64>,
    pub investing_cash_flow: Option<f64>,
    pub financing_cash_flow: Option<f64>,
    pub capex: Option<f64>,
    pub free_cash_flow: Option<f64>,
    pub ending_cash: Option<f64>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DividendEvent {
    pub end_date: String,
    pub filed_date: String,
    pub form: String,
    pub frequency_guess: String,
    pub dividend_per_share: Option<f64>,
    pub total_cash_dividends: Option<f64>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct EarningsPeriod {
    pub label: String,
    pub fiscal_year: Option<String>,
    pub fiscal_period: Option<String>,
    pub period_start: Option<String>,
    pub period_end: String,
    pub filed_date: String,
    pub form: String,
    pub revenue: Option<f64>,
    pub net_income: Option<f64>,
    pub basic_eps: Option<f64>,
    pub diluted_eps: Option<f64>,
    pub diluted_weighted_average_shares: Option<f64>,
    pub revenue_yoy_change_percent: Option<f64>,
    pub diluted_eps_yoy_change_percent: Option<f64>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct FinancialsPanelData {
    pub symbol: String,
    pub company_name: String,
    pub cik: String,
    pub frequency: Frequency,
    pub periods: Vec<StatementPeriod>,
    pub latest_filing: Option<FilingRef>,
    pub source_status: SourceStatus,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CashFlowPanelData {
    pub symbol: String,
    pub company_name: String,
    pub cik: String,
    pub frequency: Frequency,
    pub periods: Vec<CashFlowPeriod>,
    pub latest_filing: Option<FilingRef>,
    pub source_status: SourceStatus,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DividendsPanelData {
    pub symbol: String,
    pub company_name: String,
    pub cik: String,
    pub ttm_dividends_per_share: Option<f64>,
    pub ttm_common_dividends_paid: Option<f64>,
    pub latest_event: Option<DividendEvent>,
    pub events: Vec<DividendEvent>,
    pub latest_filing: Option<FilingRef>,
    pub source_status: SourceStatus,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct EarningsPanelData {
    pub symbol: String,
    pub company_name: String,
    pub cik: String,
    pub frequency: Frequency,
    pub periods: Vec<EarningsPeriod>,
    pub latest_filing: Option<FilingRef>,
    pub source_status: SourceStatus,
}
