"""Command-line interface for Stockula."""

from datetime import datetime
from enum import Enum
from typing import Annotated, Any

import typer
from pydantic import ValidationError
from rich.console import Console
from rich.panel import Panel

from .config import TickerConfig
from .config.settings import save_config
from .container import create_container
from .display import ResultsDisplay
from .manager import StockulaManager

# Create Typer app
app = typer.Typer(
    name="stockula",
    help="Stockula Trading Platform - Analyze stocks with technical analysis, backtesting, and forecasting.",
    add_completion=False,
)

# Global console instance
console = Console()


class Mode(str, Enum):
    """Operation modes for Stockula."""

    ALL = "all"
    TA = "ta"
    BACKTEST = "backtest"
    FORECAST = "forecast"
    OPTIMIZE_ALLOCATION = "optimize-allocation"


class OutputFormat(str, Enum):
    """Output format options."""

    CONSOLE = "console"
    JSON = "json"


def print_results(results: dict[str, Any], output_format: str = "console", config=None, container=None, portfolio=None):
    """Print results in specified format using ResultsDisplay.

    Args:
        results: Results dictionary
        output_format: Output format (console, json)
        config: Optional configuration object for portfolio composition
        container: Optional DI container for fetching data
        portfolio: Optional portfolio instance for forecast display
    """
    display = ResultsDisplay(console)
    display.print_results(results, output_format, config, container, portfolio)


def run_stockula(
    config: str | None = None,
    ticker: str | None = None,
    mode: str = "all",
    output: str = "console",
    save_config_path: str | None = None,
    save_optimized_config: str | None = None,
    train_start: str | None = None,
    train_end: str | None = None,
    test_start: str | None = None,
    test_end: str | None = None,
):
    """Core logic for running Stockula."""
    # Initialize DI container first
    container = create_container(config)

    # Load configuration - the container will handle this
    try:
        stockula_config = container.stockula_config()
    except ValidationError as e:
        # Use the provided config path or default
        config_path = config or ".config.yaml"
        handle_validation_error(e, config_path)

    # Set up logging based on configuration
    from .main import setup_logging

    setup_logging(stockula_config, logging_manager=container.logging_manager())

    # Override ticker if provided
    if ticker:
        stockula_config.portfolio.tickers = [TickerConfig(symbol=ticker, quantity=1.0)]
        # Disable auto-allocation for single ticker mode since we don't have categories
        stockula_config.portfolio.auto_allocate = False
        stockula_config.portfolio.dynamic_allocation = False
        stockula_config.portfolio.allocation_method = "equal_weight"
        # Allow 100% position for single ticker mode
        stockula_config.portfolio.max_position_size = 100.0

    # Override date ranges if provided
    if train_start:
        stockula_config.forecast.train_start_date = datetime.strptime(train_start, "%Y-%m-%d").date()
    if train_end:
        stockula_config.forecast.train_end_date = datetime.strptime(train_end, "%Y-%m-%d").date()
    if test_start:
        stockula_config.forecast.test_start_date = datetime.strptime(test_start, "%Y-%m-%d").date()
    if test_end:
        stockula_config.forecast.test_end_date = datetime.strptime(test_end, "%Y-%m-%d").date()

    # Create manager instance
    manager = StockulaManager(stockula_config, container, console)

    # Handle optimize-allocation mode early (before portfolio creation)
    if mode == "optimize-allocation":
        save_path = save_optimized_config or save_config_path
        return manager.run_optimize_allocation(save_path)

    # Save configuration if requested (for non-optimize-allocation modes)
    if save_config_path and mode != "optimize-allocation":
        save_config(stockula_config, save_config_path)
        print(f"Configuration saved to {save_config_path}")
        return

    # Create portfolio
    portfolio = manager.create_portfolio()

    # Display portfolio summary
    manager.display_portfolio_summary(portfolio)
    manager.display_portfolio_holdings(portfolio, mode=mode)

    # Run main processing through StockulaManager
    results = manager.run_main_processing(mode, portfolio)

    # Show current portfolio value for forecast mode
    if mode == "forecast":
        display = ResultsDisplay(console)
        display.show_portfolio_forecast_value(stockula_config, portfolio, results)

    # Output results
    output_format = output or stockula_config.output.get("format", "console")
    print_results(results, output_format, stockula_config, container, portfolio)

    # Show strategy-specific summaries after backtesting
    if mode in ["all", "backtest"] and "backtesting" in results:
        display = ResultsDisplay(console)
        display.show_strategy_summaries(manager, stockula_config, results)


@app.command()
def main(
    config: Annotated[str | None, typer.Option("--config", "-c", help="Path to configuration file (YAML)")] = None,
    ticker: Annotated[
        str | None, typer.Option("--ticker", "-t", help="Override ticker symbol (single ticker mode)")
    ] = None,
    mode: Annotated[Mode, typer.Option("--mode", "-m", help="Operation mode")] = Mode.ALL,
    output: Annotated[OutputFormat, typer.Option("--output", "-o", help="Output format")] = OutputFormat.CONSOLE,
    save_config_path: Annotated[
        str | None, typer.Option("--save-config", help="Save current configuration to file")
    ] = None,
    save_optimized_config: Annotated[
        str | None,
        typer.Option(
            "--save-optimized-config", help="Save optimized configuration to file (used with optimize-allocation mode)"
        ),
    ] = None,
    train_start: Annotated[str | None, typer.Option("--train-start", help="Training start date (YYYY-MM-DD)")] = None,
    train_end: Annotated[str | None, typer.Option("--train-end", help="Training end date (YYYY-MM-DD)")] = None,
    test_start: Annotated[str | None, typer.Option("--test-start", help="Testing start date (YYYY-MM-DD)")] = None,
    test_end: Annotated[str | None, typer.Option("--test-end", help="Testing end date (YYYY-MM-DD)")] = None,
):
    """
    Run Stockula trading analysis with various modes.

    Analyze stocks using technical indicators, backtesting strategies,
    and machine learning forecasts. Supports portfolio optimization
    and multiple output formats.
    """
    run_stockula(
        config=config,
        ticker=ticker,
        mode=mode.value,
        output=output.value,
        save_config_path=save_config_path,
        save_optimized_config=save_optimized_config,
        train_start=train_start,
        train_end=train_end,
        test_start=test_start,
        test_end=test_end,
    )


def handle_validation_error(error: ValidationError, config_path: str) -> None:
    """Handle validation errors with clean, user-friendly output.

    Args:
        error: The validation error
        config_path: Path to the configuration file
    """
    console.print("\n[bold red]Configuration Validation Error[/bold red]\n")
    console.print(f"Failed to load configuration from: [cyan]{config_path}[/cyan]\n")

    # Parse and display errors in a user-friendly format
    errors = []
    for err in error.errors():
        location = " → ".join(str(loc) for loc in err["loc"])
        message = err["msg"]

        # Clean up common error messages
        if "test_start_date must be before test_end_date" in message:
            errors.append("[yellow]Date Range Error:[/yellow] Test end date is before test start date")
        elif "train_start_date must be before train_end_date" in message:
            errors.append("[yellow]Date Range Error:[/yellow] Train end date is before train start date")
        elif "train_end_date must be before or equal to test_start_date" in message:
            errors.append("[yellow]Date Sequence Error:[/yellow] Training period must end before test period begins")
        else:
            errors.append(f"[yellow]{location}:[/yellow] {message}")

    # Display errors in a panel
    error_text = "\n".join(f"  • {err}" for err in errors)
    console.print(
        Panel(
            error_text,
            title="[bold]Validation Issues[/bold]",
            border_style="red",
            padding=(1, 2),
        )
    )

    # Show the problematic configuration section if possible
    if "backtest_optimization" in str(error):
        console.print("\n[dim]Check your backtest_optimization section in the config file.[/dim]")
        console.print("[dim]Ensure that:[/dim]")
        console.print("[dim]  • All dates are in YYYY-MM-DD format[/dim]")
        console.print("[dim]  • train_start_date < train_end_date[/dim]")
        console.print("[dim]  • test_start_date < test_end_date[/dim]")
        console.print("[dim]  • train_end_date ≤ test_start_date[/dim]")

    console.print()
    raise typer.Exit(1)


def parse_test_args():
    """Parse command line arguments for test compatibility."""
    import sys

    args = sys.argv[1:]  # Skip program name
    kwargs = {}

    # Parse arguments
    i = 0
    while i < len(args):
        arg = args[i]
        if arg == "--config" or arg == "-c":
            kwargs["config"] = args[i + 1]
            i += 2
        elif arg == "--ticker" or arg == "-t":
            kwargs["ticker"] = args[i + 1]
            i += 2
        elif arg == "--mode" or arg == "-m":
            kwargs["mode"] = args[i + 1]
            i += 2
        elif arg == "--output" or arg == "-o":
            kwargs["output"] = args[i + 1]
            i += 2
        elif arg == "--save-config":
            kwargs["save_config_path"] = args[i + 1]
            i += 2
        elif arg == "--save-optimized-config":
            kwargs["save_optimized_config"] = args[i + 1]
            i += 2
        elif arg == "--train-start":
            kwargs["train_start"] = args[i + 1]
            i += 2
        elif arg == "--train-end":
            kwargs["train_end"] = args[i + 1]
            i += 2
        elif arg == "--test-start":
            kwargs["test_start"] = args[i + 1]
            i += 2
        elif arg == "--test-end":
            kwargs["test_end"] = args[i + 1]
            i += 2
        else:
            i += 1

    return kwargs
