"""CLI commands for cost tracking.

Provides commands for:
- Viewing cost reports
- Managing budgets
- Exporting cost data
- Monitoring budget alerts
"""

from __future__ import annotations

import json
from datetime import datetime
from pathlib import Path

import click

from oclawma.cli_ui import (
    accent,
    bullet,
    header,
    highlight,
    key_value,
    print_error,
    print_info,
    print_success,
    print_warning,
    progress_spinner,
    subheader,
)
from oclawma.costs import (
    AlertLevel,
    BudgetManager,
    CostStore,
    CostTracker,
    ReportPeriod,
)


def get_data_dir() -> Path:
    """Get data directory for cost tracking."""
    data_dir = Path.home() / ".oclawma" / "costs"
    data_dir.mkdir(parents=True, exist_ok=True)
    return data_dir


def get_default_db_path() -> Path:
    """Get default cost database path."""
    return get_data_dir() / "costs.db"


@click.group(name="costs")
@click.option(
    "--db",
    "db_path",
    type=click.Path(),
    default=None,
    help="Path to cost database (default: ~/.oclawma/costs/costs.db)",
)
@click.pass_context
def costs_cli(ctx: click.Context, db_path: str | None) -> None:
    """Cost tracking and budget management.

    Track API costs, compute time, and storage usage per job.
    Manage budgets and receive alerts when limits are approached.

    \b
    Examples:
        oclawma costs report                    # Show daily cost report
        oclawma costs report --period weekly    # Show weekly report
        oclawma costs budget list               # List all budgets
        oclawma costs budget create --limit 100 # Create new budget
        oclawma costs export --format json      # Export costs to JSON
    """
    ctx.ensure_object(dict)
    ctx.obj["db_path"] = Path(db_path) if db_path else get_default_db_path()


# =============================================================================
# Report Commands
# =============================================================================


@costs_cli.command(name="report")
@click.option(
    "--period",
    type=click.Choice(["daily", "weekly", "monthly"], case_sensitive=False),
    default="daily",
    help="Report period",
    show_default=True,
)
@click.option(
    "--project",
    "project_id",
    help="Filter by project ID",
)
@click.option(
    "--tenant",
    "tenant_id",
    help="Filter by tenant ID",
)
@click.option(
    "--format",
    "output_format",
    type=click.Choice(["table", "json"], case_sensitive=False),
    default="table",
    help="Output format",
    show_default=True,
)
@click.option(
    "--output",
    "-o",
    type=click.Path(),
    help="Output file (default: stdout)",
)
@click.pass_context
def report_cmd(
    ctx: click.Context,
    period: str,
    project_id: str | None,
    tenant_id: str | None,
    output_format: str,
    output: str | None,
) -> None:
    """Generate cost attribution report.

    Shows cost breakdown by project, provider, and time period.
    """
    db_path = ctx.obj["db_path"]

    with progress_spinner("Generating report...") as _update:
        store = CostStore(db_path)
        tracker = CostTracker(store)

        try:
            report_period = ReportPeriod(period.lower())
            report = tracker.generate_report(
                period=report_period,
                project_id=project_id,
                tenant_id=tenant_id,
            )
        finally:
            tracker.close()

    if output_format == "json":
        json_output = report.to_json()
        if output:
            Path(output).write_text(json_output)
            print_success(f"Report saved to {output}")
        else:
            click.echo(json_output)
    else:
        # Table format
        click.echo(header(f"COST REPORT - {period.upper()}", width=70))
        click.echo()

        click.echo(subheader("SUMMARY"))
        click.echo(key_value("Period", report.period.value))
        click.echo(key_value("Start Date", report.start_date.strftime("%Y-%m-%d %H:%M")))
        click.echo(key_value("End Date", report.end_date.strftime("%Y-%m-%d %H:%M")))
        click.echo(key_value("Total Jobs", str(report.total_jobs)))
        click.echo(key_value("Total Cost", f"${report.total_cost:.4f}"))
        click.echo()

        if report.costs_by_project:
            click.echo(subheader("COSTS BY PROJECT"))
            for proj, cost in sorted(
                report.costs_by_project.items(), key=lambda x: x[1], reverse=True
            ):
                pct = (cost / report.total_cost * 100) if report.total_cost > 0 else 0
                click.echo(f"  {bullet(proj)}: ${cost:.4f} ({pct:.1f}%)")
            click.echo()

        if report.costs_by_tenant:
            click.echo(subheader("COSTS BY TENANT"))
            for tenant, cost in sorted(
                report.costs_by_tenant.items(), key=lambda x: x[1], reverse=True
            ):
                pct = (cost / report.total_cost * 100) if report.total_cost > 0 else 0
                click.echo(f"  {bullet(tenant)}: ${cost:.4f} ({pct:.1f}%)")
            click.echo()

        if report.costs_by_provider:
            click.echo(subheader("API COSTS BY PROVIDER"))
            for provider, cost in sorted(
                report.costs_by_provider.items(), key=lambda x: x[1], reverse=True
            ):
                pct = (cost / report.total_cost * 100) if report.total_cost > 0 else 0
                click.echo(f"  {bullet(provider)}: ${cost:.4f} ({pct:.1f}%)")
            click.echo()

        if report.daily_breakdown:
            click.echo(subheader("DAILY BREAKDOWN"))
            for date, cost in sorted(report.daily_breakdown.items()):
                bar_len = (
                    int(cost / max(report.daily_breakdown.values()) * 30)
                    if report.daily_breakdown
                    else 0
                )
                bar = "█" * bar_len
                click.echo(f"  {date}: ${cost:.4f} {accent(bar)}")
            click.echo()

        if report.top_jobs:
            click.echo(subheader("TOP JOBS BY COST"))
            for job in report.top_jobs[:5]:
                job_id = job["job_id"][:30] + "..." if len(job["job_id"]) > 30 else job["job_id"]
                click.echo(f"  {bullet(job_id)}")
                click.echo(f"    Cost: ${job['total_cost']:.4f} (API: ${job['api_cost']:.4f})")
                if job.get("project_id"):
                    click.echo(f"    Project: {job['project_id']}")
            click.echo()

        if output:
            # Save CSV format
            output_path = Path(output)
            report.to_csv(output_path)
            print_success(f"Report exported to {output_path}")


# =============================================================================
# Budget Commands
# =============================================================================


@costs_cli.group(name="budget")
@click.pass_context
def budget_cli(ctx: click.Context) -> None:
    """Manage cost budgets.

    Create and monitor budgets with automatic alerts when
    spending approaches defined thresholds.
    """
    pass


@budget_cli.command(name="list")
@click.option(
    "--project",
    "project_id",
    help="Filter by project ID",
)
@click.option(
    "--tenant",
    "tenant_id",
    help="Filter by tenant ID",
)
@click.pass_context
def budget_list(
    ctx: click.Context,
    project_id: str | None,
    tenant_id: str | None,
) -> None:
    """List all budgets."""
    db_path = ctx.obj["db_path"]
    store = CostStore(db_path)
    manager = BudgetManager(store)

    try:
        budgets = manager.list_budgets(project_id=project_id, tenant_id=tenant_id)

        if not budgets:
            print_info("No budgets found")
            return

        click.echo(header("BUDGETS", width=70))
        click.echo()

        for budget in budgets:
            usage = manager.get_budget_usage(budget.id)

            click.echo(f"{accent(budget.name)} ({highlight(budget.id)})")
            click.echo(key_value("  Monthly Limit", f"${budget.monthly_limit:.2f}"))

            if usage:
                click.echo(key_value("  Current Usage", f"${usage['current_usage']:.2f}"))
                click.echo(key_value("  Remaining", f"${usage['remaining']:.2f}"))

                # Color-code usage percentage
                pct = usage["usage_percent"]
                if pct >= 90:
                    pct_str = click.style(f"{pct:.1f}%", fg="red", bold=True)
                elif pct >= 75:
                    pct_str = click.style(f"{pct:.1f}%", fg="yellow")
                else:
                    pct_str = click.style(f"{pct:.1f}%", fg="green")
                click.echo(key_value("  Usage", pct_str))

            if budget.project_id:
                click.echo(key_value("  Project", budget.project_id))
            if budget.tenant_id:
                click.echo(key_value("  Tenant", budget.tenant_id))

            click.echo(key_value("  Alerts At", f"{budget.alert_thresholds}%"))
            click.echo()
    finally:
        store.close()


@budget_cli.command(name="create")
@click.option(
    "--name",
    "-n",
    required=True,
    help="Budget name",
)
@click.option(
    "--limit",
    "-l",
    type=float,
    required=True,
    help="Monthly spending limit (USD)",
)
@click.option(
    "--project",
    "project_id",
    help="Apply to specific project",
)
@click.option(
    "--tenant",
    "tenant_id",
    help="Apply to specific tenant",
)
@click.option(
    "--alerts",
    default="50,75,90",
    help="Alert thresholds as percentages (comma-separated)",
)
@click.option(
    "--id",
    "budget_id",
    help="Custom budget ID (optional)",
)
@click.pass_context
def budget_create(
    ctx: click.Context,
    name: str,
    limit: float,
    project_id: str | None,
    tenant_id: str | None,
    alerts: str,
    budget_id: str | None,
) -> None:
    """Create a new budget."""
    db_path = ctx.obj["db_path"]
    store = CostStore(db_path)
    manager = BudgetManager(store)

    try:
        # Parse alert thresholds
        try:
            thresholds = [int(t.strip()) for t in alerts.split(",")]
        except ValueError as err:
            print_error("Invalid alert thresholds. Use comma-separated integers.")
            raise click.Abort() from err

        budget = manager.create_budget(
            name=name,
            project_id=project_id,
            tenant_id=tenant_id,
            monthly_limit=limit,
            alert_thresholds=thresholds,
            budget_id=budget_id,
        )

        print_success(f"Budget '{name}' created with ID: {budget.id}")
        click.echo(key_value("Monthly Limit", f"${limit:.2f}"))
        click.echo(key_value("Alert Thresholds", f"{thresholds}%"))
        if project_id:
            click.echo(key_value("Project", project_id))
        if tenant_id:
            click.echo(key_value("Tenant", tenant_id))
    finally:
        store.close()


@budget_cli.command(name="update")
@click.argument("budget_id")
@click.option(
    "--name",
    help="New budget name",
)
@click.option(
    "--limit",
    type=float,
    help="New monthly limit",
)
@click.option(
    "--alerts",
    help="New alert thresholds (comma-separated)",
)
@click.pass_context
def budget_update(
    ctx: click.Context,
    budget_id: str,
    name: str | None,
    limit: float | None,
    alerts: str | None,
) -> None:
    """Update an existing budget."""
    db_path = ctx.obj["db_path"]
    store = CostStore(db_path)
    manager = BudgetManager(store)

    try:
        # Parse alert thresholds if provided
        thresholds = None
        if alerts:
            try:
                thresholds = [int(t.strip()) for t in alerts.split(",")]
            except ValueError as err:
                print_error("Invalid alert thresholds")
                raise click.Abort() from err

        budget = manager.update_budget(
            budget_id=budget_id,
            name=name,
            monthly_limit=limit,
            alert_thresholds=thresholds,
        )

        if budget:
            print_success(f"Budget '{budget_id}' updated")
        else:
            print_error(f"Budget '{budget_id}' not found")
            raise click.Abort()
    finally:
        store.close()


@budget_cli.command(name="delete")
@click.argument("budget_id")
@click.confirmation_option(prompt="Are you sure you want to delete this budget?")
@click.pass_context
def budget_delete(ctx: click.Context, budget_id: str) -> None:
    """Delete a budget."""
    db_path = ctx.obj["db_path"]
    store = CostStore(db_path)
    manager = BudgetManager(store)

    try:
        if manager.delete_budget(budget_id):
            print_success(f"Budget '{budget_id}' deleted")
        else:
            print_error(f"Budget '{budget_id}' not found")
            raise click.Abort()
    finally:
        store.close()


@budget_cli.command(name="check")
@click.argument("budget_id", required=False)
@click.pass_context
def budget_check(ctx: click.Context, budget_id: str | None) -> None:
    """Check budget status and show alerts.

    If BUDGET_ID is provided, checks only that budget.
    Otherwise, checks all budgets.
    """
    db_path = ctx.obj["db_path"]
    store = CostStore(db_path)
    manager = BudgetManager(store)

    try:
        if budget_id:
            # Check specific budget
            budget = manager.get_budget(budget_id)
            if not budget:
                print_error(f"Budget '{budget_id}' not found")
                raise click.Abort()

            alerts = manager.check_budget(budget_id)
            usage = manager.get_budget_usage(budget_id)

            click.echo(header(f"BUDGET: {budget.name}", width=70))
            click.echo()

            if usage:
                click.echo(key_value("Limit", f"${usage['monthly_limit']:.2f}"))
                click.echo(key_value("Current Usage", f"${usage['current_usage']:.2f}"))
                click.echo(key_value("Remaining", f"${usage['remaining']:.2f}"))

                pct = usage["usage_percent"]
                if pct >= 90:
                    pct_str = click.style(f"{pct:.1f}%", fg="red", bold=True)
                elif pct >= 75:
                    pct_str = click.style(f"{pct:.1f}%", fg="yellow")
                else:
                    pct_str = click.style(f"{pct:.1f}%", fg="green")
                click.echo(key_value("Usage", pct_str))
            click.echo()

            if alerts:
                click.echo(subheader("ALERTS"))
                for alert in alerts:
                    if alert.level == AlertLevel.CRITICAL:
                        print_error(f"  🚨 {alert.message}")
                    elif alert.level == AlertLevel.WARNING:
                        print_warning(f"  ⚠️  {alert.message}")
                    else:
                        print_info(f"  ℹ️  {alert.message}")
            else:
                print_success("No alerts - budget is healthy")
        else:
            # Check all budgets
            click.echo(header("BUDGET STATUS CHECK", width=70))
            click.echo()

            results = manager.check_all_budgets()

            if not results:
                print_success("All budgets are healthy - no alerts")
            else:
                for bid, alerts in results.items():
                    budget = manager.get_budget(bid)
                    name = budget.name if budget else bid
                    click.echo(f"\n{accent(name)}:")
                    for alert in alerts:
                        if alert.level == AlertLevel.CRITICAL:
                            print_error(f"  🚨 {alert.message}")
                        elif alert.level == AlertLevel.WARNING:
                            print_warning(f"  ⚠️  {alert.message}")
                        else:
                            print_info(f"  ℹ️  {alert.message}")
    finally:
        store.close()


@budget_cli.command(name="usage")
@click.argument("budget_id")
@click.pass_context
def budget_usage(ctx: click.Context, budget_id: str) -> None:
    """Show detailed usage for a budget."""
    db_path = ctx.obj["db_path"]
    store = CostStore(db_path)
    manager = BudgetManager(store)

    try:
        budget = manager.get_budget(budget_id)
        if not budget:
            print_error(f"Budget '{budget_id}' not found")
            raise click.Abort()

        usage = manager.get_budget_usage(budget_id)
        if not usage:
            print_error("Could not retrieve usage data")
            raise click.Abort()

        click.echo(header(f"BUDGET USAGE: {budget.name}", width=70))
        click.echo()

        # Visual bar
        pct = usage["usage_percent"]
        bar_width = 50
        filled = int(pct / 100 * bar_width)
        bar = "█" * filled + "░" * (bar_width - filled)

        if pct >= 90:
            bar = click.style(bar, fg="red")
        elif pct >= 75:
            bar = click.style(bar, fg="yellow")
        else:
            bar = click.style(bar, fg="green")

        click.echo(f"  [{bar}] {pct:.1f}%")
        click.echo()

        click.echo(key_value("Monthly Limit", f"${usage['monthly_limit']:.2f}"))
        click.echo(key_value("Current Usage", f"${usage['current_usage']:.2f}"))
        click.echo(key_value("Remaining", f"${usage['remaining']:.2f}"))
        click.echo()

        if budget.project_id:
            click.echo(key_value("Project Filter", budget.project_id))
        if budget.tenant_id:
            click.echo(key_value("Tenant Filter", budget.tenant_id))
    finally:
        store.close()


# =============================================================================
# Export Commands
# =============================================================================


@costs_cli.command(name="export")
@click.option(
    "--format",
    "output_format",
    type=click.Choice(["json", "csv"], case_sensitive=False),
    default="json",
    help="Export format",
    show_default=True,
)
@click.option(
    "--output",
    "-o",
    type=click.Path(),
    required=True,
    help="Output file path",
)
@click.option(
    "--project",
    "project_id",
    help="Filter by project ID",
)
@click.option(
    "--tenant",
    "tenant_id",
    help="Filter by tenant ID",
)
@click.option(
    "--since",
    help="Start date (YYYY-MM-DD)",
)
@click.option(
    "--until",
    help="End date (YYYY-MM-DD)",
)
@click.pass_context
def export_cmd(
    ctx: click.Context,
    output_format: str,
    output: str,
    project_id: str | None,
    tenant_id: str | None,
    since: str | None,
    until: str | None,
) -> None:
    """Export cost data to file.

    Exports job costs in JSON or CSV format for analysis
    or integration with external tools.
    """
    db_path = ctx.obj["db_path"]
    store = CostStore(db_path)

    try:
        # Parse dates
        start_date = None
        end_date = None
        if since:
            start_date = datetime.strptime(since, "%Y-%m-%d")
        if until:
            end_date = datetime.strptime(until, "%Y-%m-%d")

        # Get job costs
        jobs = store.get_job_costs(
            project_id=project_id,
            tenant_id=tenant_id,
            start_date=start_date,
            end_date=end_date,
            limit=10000,
        )

        output_path = Path(output)

        if output_format == "json":
            # Export as JSON
            data = {
                "exported_at": datetime.utcnow().isoformat(),
                "count": len(jobs),
                "filters": {
                    "project_id": project_id,
                    "tenant_id": tenant_id,
                    "since": since,
                    "until": until,
                },
                "jobs": [job.to_dict() for job in jobs],
            }
            output_path.write_text(json.dumps(data, indent=2))
        else:
            # Export as CSV
            import csv

            with open(output_path, "w", newline="") as f:
                writer = csv.writer(f)
                writer.writerow(
                    [
                        "job_id",
                        "project_id",
                        "tenant_id",
                        "created_at",
                        "completed_at",
                        "compute_time_seconds",
                        "total_api_cost",
                        "storage_bytes",
                        "total_cost",
                        "total_tokens",
                    ]
                )
                for job in jobs:
                    writer.writerow(
                        [
                            job.job_id,
                            job.project_id or "",
                            job.tenant_id or "",
                            job.created_at.isoformat(),
                            job.completed_at.isoformat() if job.completed_at else "",
                            job.compute_time_seconds,
                            job.total_api_cost,
                            job.storage_bytes,
                            job.total_cost,
                            job.total_tokens,
                        ]
                    )

        print_success(f"Exported {len(jobs)} job costs to {output_path}")
    finally:
        store.close()


# =============================================================================
# Job Commands
# =============================================================================


@costs_cli.command(name="show")
@click.argument("job_id")
@click.pass_context
def show_job(ctx: click.Context, job_id: str) -> None:
    """Show detailed cost information for a specific job."""
    db_path = ctx.obj["db_path"]
    store = CostStore(db_path)

    try:
        job = store.get_job_cost(job_id)
        if not job:
            print_error(f"Job '{job_id}' not found")
            raise click.Abort()

        click.echo(header(f"JOB COST: {job_id}", width=70))
        click.echo()

        click.echo(subheader("BASIC INFO"))
        if job.project_id:
            click.echo(key_value("Project", job.project_id))
        if job.tenant_id:
            click.echo(key_value("Tenant", job.tenant_id))
        click.echo(key_value("Created", job.created_at.strftime("%Y-%m-%d %H:%M:%S")))
        if job.completed_at:
            click.echo(key_value("Completed", job.completed_at.strftime("%Y-%m-%d %H:%M:%S")))
            click.echo(key_value("Duration", f"{job.duration_seconds:.2f}s"))
        click.echo()

        click.echo(subheader("COST BREAKDOWN"))
        click.echo(key_value("API Costs", f"${job.total_api_cost:.4f}"))
        click.echo(key_value("Compute Time", f"{job.compute_time_seconds:.2f}s"))
        click.echo(key_value("Storage", f"{job.storage_bytes:,} bytes"))
        click.echo(key_value("Total Cost", f"${job.total_cost:.4f}"))
        click.echo()

        if job.api_costs:
            click.echo(subheader("API CALLS"))
            for call in job.api_costs:
                click.echo(f"  {bullet(call.provider)}/{call.model}:")
                click.echo(f"    Cost: ${call.cost:.4f}")
                if call.tokens_input or call.tokens_output:
                    click.echo(f"    Tokens: {call.tokens_input} in / {call.tokens_output} out")
            click.echo()

        if job.metadata:
            click.echo(subheader("METADATA"))
            for key, value in job.metadata.items():
                click.echo(key_value(f"  {key}", str(value)))
    finally:
        store.close()


@costs_cli.command(name="list-jobs")
@click.option(
    "--project",
    "project_id",
    help="Filter by project ID",
)
@click.option(
    "--tenant",
    "tenant_id",
    help="Filter by tenant ID",
)
@click.option(
    "--limit",
    type=int,
    default=20,
    help="Maximum number of jobs to show",
    show_default=True,
)
@click.pass_context
def list_jobs(
    ctx: click.Context,
    project_id: str | None,
    tenant_id: str | None,
    limit: int,
) -> None:
    """List recent jobs with cost information."""
    db_path = ctx.obj["db_path"]
    store = CostStore(db_path)

    try:
        jobs = store.get_job_costs(
            project_id=project_id,
            tenant_id=tenant_id,
            limit=limit,
        )

        if not jobs:
            print_info("No jobs found")
            return

        click.echo(header(f"RECENT JOBS (showing {len(jobs)})", width=70))
        click.echo()

        for job in jobs:
            job_id = job.job_id[:40] + "..." if len(job.job_id) > 40 else job.job_id
            click.echo(f"{accent(job_id)}")
            click.echo(key_value("  Cost", f"${job.total_cost:.4f}"))
            if job.project_id:
                click.echo(key_value("  Project", job.project_id))
            click.echo(key_value("  Created", job.created_at.strftime("%Y-%m-%d %H:%M")))
            click.echo()
    finally:
        store.close()
