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,
    },
    BalanceSheet {
        data: BalanceSheetPanelData,
    },
    CashFlow {
        data: CashFlowPanelData,
    },
    Dividends {
        data: DividendsPanelData,
    },
    Earnings {
        data: EarningsPanelData,
    },
    Filings {
        data: FilingsPanelData,
    },
}

/// 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 FilingListItem {
    pub accession_number: String,
    pub filing_date: String,
    pub report_date: Option<String>,
    pub form: String,
    pub primary_document: String,
    pub filing_url: String,
    pub overview: String,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct SourceStatus {
    pub companyfacts_used: 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 short_term_investments: Option<f64>,
    pub accounts_receivable_net: Option<f64>,
    pub inventory: Option<f64>,
    pub other_current_assets: Option<f64>,
    pub total_current_assets: Option<f64>,
    pub property_plant_and_equipment_net: Option<f64>,
    pub goodwill: Option<f64>,
    pub intangible_assets: Option<f64>,
    pub other_non_current_assets: Option<f64>,
    pub total_assets: Option<f64>,
    pub accounts_payable: Option<f64>,
    pub accrued_expenses_and_other_current_liabilities: Option<f64>,
    pub short_term_debt: Option<f64>,
    pub total_current_liabilities: Option<f64>,
    pub long_term_debt: Option<f64>,
    pub other_non_current_liabilities: Option<f64>,
    pub total_liabilities: Option<f64>,
    pub retained_earnings: Option<f64>,
    pub accumulated_other_comprehensive_income_loss: Option<f64>,
    pub total_equity: Option<f64>,
    pub shares_outstanding: Option<f64>,
    pub book_value_per_share: 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,
    // Operating Activities
    pub net_income: Option<f64>,
    pub depreciation_and_amortization: Option<f64>,
    pub stock_based_compensation: Option<f64>,
    pub deferred_income_taxes: Option<f64>,
    pub impairment_charges: Option<f64>,
    pub loss_gain_on_sale_of_assets: Option<f64>,
    pub other_non_cash_items: Option<f64>,
    pub accounts_receivable: Option<f64>,
    pub inventory: Option<f64>,
    pub prepaid_expenses_and_other_current_assets: Option<f64>,
    pub accounts_payable: Option<f64>,
    pub accrued_expenses: Option<f64>,
    pub deferred_revenue: Option<f64>,
    pub other_working_capital_changes: Option<f64>,
    pub change_in_operating_assets: Option<f64>,
    pub change_in_operating_liabilities: Option<f64>,
    pub other_operating_adjustments: Option<f64>,
    pub operating_cash_flow: Option<f64>,
    // Investing Activities
    pub capex: Option<f64>,
    pub proceeds_from_sale_of_ppe: Option<f64>,
    pub purchases_of_investments: Option<f64>,
    pub sales_of_investments: Option<f64>,
    pub acquisitions: Option<f64>,
    pub other_investing_activities: Option<f64>,
    pub investing_cash_flow: Option<f64>,
    // Financing Activities
    pub debt_issuance: Option<f64>,
    pub debt_repayment: Option<f64>,
    pub equity_issuance: Option<f64>,
    pub stock_repurchases: Option<f64>,
    pub dividends_paid: Option<f64>,
    pub finance_lease_obligations: Option<f64>,
    pub other_financing_activities: Option<f64>,
    pub financing_cash_flow: Option<f64>,
    // Summary
    pub free_cash_flow: Option<f64>,
    pub exchange_rate_effects: Option<f64>,
    pub net_increase_decrease_cash: Option<f64>,
    pub beginning_cash: Option<f64>,
    pub ending_cash: Option<f64>,
    pub cash_paid_interest: Option<f64>,
    pub cash_paid_income_taxes: 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 display_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 total_revenues: Option<f64>,
    pub total_revenues_yoy_change_percent: Option<f64>,
    pub cost_of_sales: Option<f64>,
    pub gross_profit: Option<f64>,
    pub gross_profit_margin: Option<f64>,
    pub selling_general_and_administrative_expenses: Option<f64>,
    pub research_and_development_expenses: Option<f64>,
    pub other_operating_expenses: Option<f64>,
    pub operating_profit: Option<f64>,
    pub operating_margin: Option<f64>,
    pub non_operating_income: Option<f64>,
    pub total_non_operating_income: Option<f64>,
    pub income_before_provision_for_income_taxes: Option<f64>,
    pub provision_for_income_taxes: Option<f64>,
    pub consolidated_net_income: Option<f64>,
    pub net_income_attributable_to_common_shareholders: Option<f64>,
    pub total_shares_outstanding: Option<f64>,
    pub basic_weighted_average_shares_outstanding: Option<f64>,
    pub diluted_weighted_average_shares_outstanding: Option<f64>,
    pub ebitda: Option<f64>,
    pub effective_tax_rate: Option<f64>,
    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, Eq)]
#[serde(rename_all = "camelCase")]
pub struct EarningsPeriodOption {
    pub id: String,
    pub label: String,
    pub frequency: Frequency,
    pub fiscal_year: Option<String>,
    pub fiscal_period: Option<String>,
    pub period_end: String,
    pub sort_key: String,
}

#[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,
    pub available_concepts: Vec<AvailableXbrlConcept>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub mapping_warnings: Vec<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct BalanceSheetPanelData {
    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,
    pub available_concepts: Vec<AvailableXbrlConcept>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub selected_currency: Option<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub available_currencies: Vec<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub mapping_warnings: Vec<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub available_periods: Vec<EarningsPeriodOption>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub selected_period_start: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub selected_period_end: Option<String>,
}

#[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,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub available_periods: Vec<EarningsPeriodOption>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub selected_period_start: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub selected_period_end: Option<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub mapping_warnings: Vec<String>,
}

#[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 available_periods: Vec<EarningsPeriodOption>,
    pub selected_period_start: Option<String>,
    pub selected_period_end: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub selected_currency: Option<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub available_currencies: Vec<String>,
    pub latest_filing: Option<FilingRef>,
    pub source_status: SourceStatus,
    pub available_concepts: Vec<AvailableXbrlConcept>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub mapping_warnings: Vec<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct FilingsPanelData {
    pub symbol: String,
    pub company_name: String,
    pub cik: String,
    pub items: Vec<FilingListItem>,
    pub page: usize,
    pub page_size: usize,
    pub total_results: usize,
    pub total_pages: usize,
    pub query: Option<String>,
    pub latest_filing: Option<FilingListItem>,
}

/// Represents an XBRL concept available in the filing for custom mapping.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AvailableXbrlConcept {
    /// The taxonomy namespace (e.g., "us-gaap", "ifrs-full")
    pub taxonomy: String,
    /// The concept name within the taxonomy
    pub concept: String,
    /// The unit family for this concept
    pub unit_family: String,
    /// A human-readable label for the concept (if available)
    pub label: Option<String>,
    /// The period this concept is available for (by period ID)
    pub period_id: String,
    /// The numeric value of this concept fact, if available
    pub value: Option<f64>,
}
