#!/usr/bin/env python3
"""
Financial Ratio Calculator

Calculates and interprets financial ratios across 5 categories:
profitability, liquidity, leverage, efficiency, and valuation.

Usage:
    python ratio_calculator.py financial_data.json
    python ratio_calculator.py financial_data.json --format json
    python ratio_calculator.py financial_data.json --category profitability
"""

import argparse
import json
import sys
from typing import Any, Dict, List, Optional, Tuple


def safe_divide(numerator: float, denominator: float, default: float = 0.0) -> float:
    """Safely divide two numbers, returning default if denominator is zero."""
    if denominator == 0 or denominator is None:
        return default
    return numerator / denominator


def resolve_input_section(
    data: Dict[str, Any], section_key: str, flat_keys: Tuple[str, ...]
) -> Dict[str, Any]:
    """
    Accept both supported input shapes:
    1. Flat: the expected keys live at the top level of the JSON file.
    2. Nested: the data lives under a per-tool section key, as in
       assets/sample_financial_data.json (which bundles inputs for all
       four financial-analyst scripts in one file).
    """
    if any(key in data for key in flat_keys):
        return data
    section = data.get(section_key)
    if isinstance(section, dict):
        return section
    return data


class FinancialRatioCalculator:
    """Calculate and interpret financial ratios from statement data."""

    # Industry benchmark ranges: (low, typical, high)
    BENCHMARKS: Dict[str, Tuple[float, float, float]] = {
        "roe": (0.08, 0.15, 0.25),
        "roa": (0.03, 0.06, 0.12),
        "gross_margin": (0.25, 0.40, 0.60),
        "operating_margin": (0.05, 0.15, 0.25),
        "net_margin": (0.03, 0.10, 0.20),
        "current_ratio": (1.0, 1.5, 3.0),
        "quick_ratio": (0.8, 1.0, 2.0),
        "cash_ratio": (0.2, 0.5, 1.0),
        "debt_to_equity": (0.3, 0.8, 2.0),
        "interest_coverage": (2.0, 5.0, 10.0),
        "dscr": (1.0, 1.5, 2.5),
        "asset_turnover": (0.5, 1.0, 2.0),
        "inventory_turnover": (4.0, 8.0, 12.0),
        "receivables_turnover": (6.0, 10.0, 15.0),
        "dso": (30.0, 45.0, 60.0),
        "pe_ratio": (10.0, 20.0, 35.0),
        "pb_ratio": (1.0, 2.5, 5.0),
        "ps_ratio": (1.0, 3.0, 8.0),
        "ev_ebitda": (6.0, 12.0, 20.0),
        "peg_ratio": (0.5, 1.0, 2.0),
    }

    def __init__(self, data: Dict[str, Any]) -> None:
        """Initialize with financial statement data."""
        self.income = data.get("income_statement", {})
        self.balance = data.get("balance_sheet", {})
        self.cash_flow = data.get("cash_flow", {})
        self.market = data.get("market_data", {})
        self.results: Dict[str, Dict[str, Any]] = {}

    def calculate_profitability(self) -> Dict[str, Any]:
        """Calculate profitability ratios."""
        revenue = self.income.get("revenue", 0)
        cogs = self.income.get("cost_of_goods_sold", 0)
        operating_income = self.income.get("operating_income", 0)
        net_income = self.income.get("net_income", 0)
        total_equity = self.balance.get("total_equity", 0)
        total_assets = self.balance.get("total_assets", 0)

        gross_profit = revenue - cogs

        ratios = {
            "roe": {
                "value": safe_divide(net_income, total_equity),
                "formula": "Net Income / Total Equity",
                "name": "Return on Equity",
            },
            "roa": {
                "value": safe_divide(net_income, total_assets),
                "formula": "Net Income / Total Assets",
                "name": "Return on Assets",
            },
            "gross_margin": {
                "value": safe_divide(gross_profit, revenue),
                "formula": "(Revenue - COGS) / Revenue",
                "name": "Gross Margin",
            },
            "operating_margin": {
                "value": safe_divide(operating_income, revenue),
                "formula": "Operating Income / Revenue",
                "name": "Operating Margin",
            },
            "net_margin": {
                "value": safe_divide(net_income, revenue),
                "formula": "Net Income / Revenue",
                "name": "Net Margin",
            },
        }

        for key, ratio in ratios.items():
            ratio["interpretation"] = self.interpret_ratio(key, ratio["value"])

        self.results["profitability"] = ratios
        return ratios

    def calculate_liquidity(self) -> Dict[str, Any]:
        """Calculate liquidity ratios."""
        current_assets = self.balance.get("current_assets", 0)
        current_liabilities = self.balance.get("current_liabilities", 0)
        inventory = self.balance.get("inventory", 0)
        cash = self.balance.get("cash_and_equivalents", 0)

        ratios = {
            "current_ratio": {
                "value": safe_divide(current_assets, current_liabilities),
                "formula": "Current Assets / Current Liabilities",
                "name": "Current Ratio",
            },
            "quick_ratio": {
                "value": safe_divide(
                    current_assets - inventory, current_liabilities
                ),
                "formula": "(Current Assets - Inventory) / Current Liabilities",
                "name": "Quick Ratio",
            },
            "cash_ratio": {
                "value": safe_divide(cash, current_liabilities),
                "formula": "Cash & Equivalents / Current Liabilities",
                "name": "Cash Ratio",
            },
        }

        for key, ratio in ratios.items():
            ratio["interpretation"] = self.interpret_ratio(key, ratio["value"])

        self.results["liquidity"] = ratios
        return ratios

    def calculate_leverage(self) -> Dict[str, Any]:
        """Calculate leverage ratios."""
        total_debt = self.balance.get("total_debt", 0)
        total_equity = self.balance.get("total_equity", 0)
        operating_income = self.income.get("operating_income", 0)
        interest_expense = self.income.get("interest_expense", 0)
        operating_cash_flow = self.cash_flow.get("operating_cash_flow", 0)
        total_debt_service = self.cash_flow.get(
            "total_debt_service", interest_expense
        )

        ratios = {
            "debt_to_equity": {
                "value": safe_divide(total_debt, total_equity),
                "formula": "Total Debt / Total Equity",
                "name": "Debt-to-Equity Ratio",
            },
            "interest_coverage": {
                "value": safe_divide(operating_income, interest_expense),
                "formula": "Operating Income / Interest Expense",
                "name": "Interest Coverage Ratio",
            },
            "dscr": {
                "value": safe_divide(operating_cash_flow, total_debt_service),
                "formula": "Operating Cash Flow / Total Debt Service",
                "name": "Debt Service Coverage Ratio",
            },
        }

        for key, ratio in ratios.items():
            ratio["interpretation"] = self.interpret_ratio(key, ratio["value"])

        self.results["leverage"] = ratios
        return ratios

    def calculate_efficiency(self) -> Dict[str, Any]:
        """Calculate efficiency ratios."""
        revenue = self.income.get("revenue", 0)
        cogs = self.income.get("cost_of_goods_sold", 0)
        total_assets = self.balance.get("total_assets", 0)
        inventory = self.balance.get("inventory", 0)
        accounts_receivable = self.balance.get("accounts_receivable", 0)

        receivables_turnover_val = safe_divide(revenue, accounts_receivable)

        ratios = {
            "asset_turnover": {
                "value": safe_divide(revenue, total_assets),
                "formula": "Revenue / Total Assets",
                "name": "Asset Turnover",
            },
            "inventory_turnover": {
                "value": safe_divide(cogs, inventory),
                "formula": "COGS / Inventory",
                "name": "Inventory Turnover",
            },
            "receivables_turnover": {
                "value": receivables_turnover_val,
                "formula": "Revenue / Accounts Receivable",
                "name": "Receivables Turnover",
            },
            "dso": {
                "value": safe_divide(365, receivables_turnover_val)
                if receivables_turnover_val > 0
                else 0.0,
                "formula": "365 / Receivables Turnover",
                "name": "Days Sales Outstanding",
            },
        }

        for key, ratio in ratios.items():
            ratio["interpretation"] = self.interpret_ratio(key, ratio["value"])

        self.results["efficiency"] = ratios
        return ratios

    def calculate_valuation(self) -> Dict[str, Any]:
        """Calculate valuation ratios (requires market data)."""
        market_cap = self.market.get("market_cap", 0)
        share_price = self.market.get("share_price", 0)
        shares_outstanding = self.market.get("shares_outstanding", 0)
        earnings_growth_rate = self.market.get("earnings_growth_rate", 0)

        net_income = self.income.get("net_income", 0)
        revenue = self.income.get("revenue", 0)
        total_equity = self.balance.get("total_equity", 0)
        total_debt = self.balance.get("total_debt", 0)
        cash = self.balance.get("cash_and_equivalents", 0)
        ebitda = self.income.get("ebitda", 0)

        if market_cap == 0 and share_price > 0 and shares_outstanding > 0:
            market_cap = share_price * shares_outstanding

        eps = safe_divide(net_income, shares_outstanding)
        book_value_per_share = safe_divide(total_equity, shares_outstanding)
        enterprise_value = market_cap + total_debt - cash
        pe = safe_divide(share_price, eps)

        ratios = {
            "pe_ratio": {
                "value": pe,
                "formula": "Share Price / Earnings Per Share",
                "name": "Price-to-Earnings Ratio",
            },
            "pb_ratio": {
                "value": safe_divide(share_price, book_value_per_share),
                "formula": "Share Price / Book Value Per Share",
                "name": "Price-to-Book Ratio",
            },
            "ps_ratio": {
                "value": safe_divide(
                    market_cap, revenue
                ),
                "formula": "Market Cap / Revenue",
                "name": "Price-to-Sales Ratio",
            },
            "ev_ebitda": {
                "value": safe_divide(enterprise_value, ebitda),
                "formula": "Enterprise Value / EBITDA",
                "name": "EV/EBITDA",
            },
            "peg_ratio": {
                "value": safe_divide(pe, earnings_growth_rate * 100)
                if earnings_growth_rate > 0
                else 0.0,
                "formula": "P/E Ratio / Earnings Growth Rate (%)",
                "name": "PEG Ratio",
            },
        }

        for key, ratio in ratios.items():
            ratio["interpretation"] = self.interpret_ratio(key, ratio["value"])

        self.results["valuation"] = ratios
        return ratios

    def calculate_all(self) -> Dict[str, Dict[str, Any]]:
        """Calculate all ratio categories."""
        self.calculate_profitability()
        self.calculate_liquidity()
        self.calculate_leverage()
        self.calculate_efficiency()
        self.calculate_valuation()
        return self.results

    def interpret_ratio(self, ratio_key: str, value: float) -> str:
        """Interpret a ratio value against benchmarks."""
        if value == 0.0:
            return "Insufficient data to calculate"

        benchmarks = self.BENCHMARKS.get(ratio_key)
        if not benchmarks:
            return "No benchmark available"

        low, typical, high = benchmarks

        # DSO is inverse - lower is better
        if ratio_key == "dso":
            if value <= low:
                return "Excellent - collections well above average"
            elif value <= typical:
                return "Good - collections within normal range"
            elif value <= high:
                return "Acceptable - monitor collection trends"
            else:
                return "Concern - collections significantly slower than peers"

        # Debt-to-equity - lower generally better (but context matters)
        if ratio_key == "debt_to_equity":
            if value <= low:
                return "Conservative leverage - strong equity position"
            elif value <= typical:
                return "Moderate leverage - well balanced"
            elif value <= high:
                return "Elevated leverage - monitor debt levels"
            else:
                return "High leverage - potential financial risk"

        # Standard interpretation (higher is better for most ratios)
        if value < low:
            return "Below average - needs improvement"
        elif value <= typical:
            return "Acceptable - within normal range"
        elif value <= high:
            return "Good - above average performance"
        else:
            return "Excellent - significantly above peers"

    @staticmethod
    def format_ratio(value: float, is_percentage: bool = False) -> str:
        """Format a ratio value for display."""
        if is_percentage:
            return f"{value * 100:.1f}%"
        return f"{value:.2f}"

    def format_text(self, category: Optional[str] = None) -> str:
        """Format results as human-readable text."""
        lines: List[str] = []
        lines.append("=" * 70)
        lines.append("FINANCIAL RATIO ANALYSIS")
        lines.append("=" * 70)

        categories = (
            {category: self.results[category]}
            if category and category in self.results
            else self.results
        )

        percentage_ratios = {
            "roe", "roa", "gross_margin", "operating_margin", "net_margin"
        }

        for cat_name, ratios in categories.items():
            lines.append(f"\n--- {cat_name.upper()} ---")
            for key, ratio in ratios.items():
                is_pct = key in percentage_ratios
                formatted = self.format_ratio(ratio["value"], is_pct)
                lines.append(f"  {ratio['name']}: {formatted}")
                lines.append(f"    Formula: {ratio['formula']}")
                lines.append(f"    Assessment: {ratio['interpretation']}")

        lines.append("\n" + "=" * 70)
        return "\n".join(lines)

    def to_json(self, category: Optional[str] = None) -> Dict[str, Any]:
        """Return results as JSON-serializable dict."""
        if category and category in self.results:
            return {"category": category, "ratios": self.results[category]}
        return {"categories": self.results}


def main() -> None:
    """Main entry point."""
    parser = argparse.ArgumentParser(
        description="Calculate and interpret financial ratios"
    )
    parser.add_argument(
        "input_file",
        help="Path to JSON file with financial statement data",
    )
    parser.add_argument(
        "--format",
        choices=["text", "json"],
        default="text",
        help="Output format (default: text)",
    )
    parser.add_argument(
        "--category",
        choices=[
            "profitability",
            "liquidity",
            "leverage",
            "efficiency",
            "valuation",
        ],
        default=None,
        help="Calculate only a specific ratio category",
    )

    args = parser.parse_args()

    try:
        with open(args.input_file, "r") as f:
            data = json.load(f)
    except FileNotFoundError:
        print(f"Error: File '{args.input_file}' not found.", file=sys.stderr)
        sys.exit(1)
    except json.JSONDecodeError as e:
        print(f"Error: Invalid JSON in '{args.input_file}': {e}", file=sys.stderr)
        sys.exit(1)

    flat_keys = ("income_statement", "balance_sheet", "cash_flow", "market_data")
    data = resolve_input_section(data, "ratio_analysis", flat_keys)
    if not any(key in data for key in flat_keys):
        print(
            "Error: No financial statement data found. Expected "
            f"{', '.join(flat_keys)} at the top level or nested under "
            "'ratio_analysis'.",
            file=sys.stderr,
        )
        sys.exit(1)

    calculator = FinancialRatioCalculator(data)

    if args.category:
        method_map = {
            "profitability": calculator.calculate_profitability,
            "liquidity": calculator.calculate_liquidity,
            "leverage": calculator.calculate_leverage,
            "efficiency": calculator.calculate_efficiency,
            "valuation": calculator.calculate_valuation,
        }
        method_map[args.category]()
    else:
        calculator.calculate_all()

    if args.format == "json":
        print(json.dumps(calculator.to_json(args.category), indent=2))
    else:
        print(calculator.format_text(args.category))


if __name__ == "__main__":
    main()
