"""Output formatting for CLI results (Layer 2).

Formats structured SDK events for agent consumption.

Progress events print immediately as they arrive (via on_event callbacks).
Completion events print the final result after the stream closes.
"""

from __future__ import annotations

from typing import Any

from .client import StreamResult
from .parse import SDKEvent, parse_json_field

# ---- Shared formatters ----


def format_file_line(f: dict[str, Any]) -> str:
    """Format a single file as a one-line summary."""
    file_id: str = f.get('id', '?')
    name: str = f.get('name', '?')
    records = ', '.join(parse_json_field(f.get('records')))
    aggs = ', '.join(parse_json_field(f.get('aggregations')))
    freqs = ', '.join(parse_json_field(f.get('frequencies')))

    # Handle both JSONL event format and D1 format for date range
    dr = f.get('date_range')
    dr_min = f.get('date_range_min')
    dr_max = f.get('date_range_max')
    if dr and isinstance(dr, dict):
        date_str = f'{dr["min"][:7]} -> {dr["max"][:7]}'
    elif dr_min and dr_max:
        date_str = f'{str(dr_min)[:7]} -> {str(dr_max)[:7]}'
    else:
        date_str = '-'

    created_at = str(f.get('created_at', ''))[:16]  # "2026-03-17 23:47"
    parts = [f'  {file_id}', name, records, aggs, freqs, date_str]
    if created_at:
        parts.append(created_at)
    return '  '.join(parts)


# ---- Streaming callbacks (print as events arrive) ----


def on_files_event(event: SDKEvent, result: StreamResult) -> None:
    """Print files progress events as they arrive."""
    name = event['event']
    data = event['data']

    if name == 'FilesDiscovered':
        count = len(data.get('files', []))
        run_id = result.run_id or ''
        prefix = f'{run_id}: ' if run_id else ''
        print(f'{prefix}classifying {count} file{"s" if count != 1 else ""}', flush=True)

    elif name == 'FilesClassificationCompleted':
        files: list[dict[str, Any]] = data.get('files', [])
        for f in files:
            print(format_file_line(f), flush=True)

    elif name == 'FilesClassificationFailed':
        error: str = data.get('error', 'unknown error')
        print(f'error: classification failed — {error}', flush=True)


def on_forecast_event(event: SDKEvent, result: StreamResult) -> None:
    """Print forecast progress events as they arrive."""
    name = event['event']
    data = event['data']

    if name == 'FilesDiscovered':
        count = len(data.get('files', []))
        run_id = result.run_id or ''
        prefix = f'{run_id}: ' if run_id else ''
        print(f'{prefix}classifying {count} file{"s" if count != 1 else ""}', flush=True)

    elif name == 'FilesClassificationCompleted':
        files: list[dict[str, Any]] = data.get('files', [])
        print(f'classified {len(files)} file{"s" if len(files) != 1 else ""}', flush=True)

    elif name == 'SeriesExtractionStarted':
        print('extracting series', flush=True)

    elif name == 'ModelingStarted':
        series: list[dict[str, Any]] = data.get('series', [])
        print(f'training {len(series)} model{"s" if len(series) != 1 else ""}', flush=True)

    elif name == 'ForecastingStarted':
        series = data.get('series', [])
        print(f'forecasting {len(series)} series', flush=True)

    elif name == 'ForecastGenCompleted':
        series_name = data.get('series_name', data.get('series_id', '?'))
        print(f'  \u2713  {series_name}', flush=True)

    elif name == 'ForecastGenFailed' or name == 'TargetDataFailed':
        series_name = data.get('series_name', data.get('series_id', '?'))
        error = data.get('error', 'unknown error')
        print(f'  \u2717  {series_name}  {error}', flush=True)

    elif name == 'FilesClassificationFailed':
        error_msg: str = data.get('error', 'unknown error')
        print(f'error: classification failed — {error_msg}', flush=True)


# ---- Final result printers (after stream closes) ----


def print_files_result(result: StreamResult, elapsed: float) -> int:
    """Print final footer for files command. Returns exit code.

    Progress events are already printed by on_files_event during streaming.
    This only handles errors and the footer.
    """
    code = result.exit_code if result.exit_code is not None else 1

    if result.errors:
        for err in result.errors:
            print(f'error: {err}')
        print(f'[exit:{code} | {elapsed:.1f}s]')
        return code

    if code != 0 and not result.get_event('FilesClassificationFailed') and result.stderr.strip():
        print(f'error: {result.stderr.strip().splitlines()[-1]}')

    print(f'[exit:{code} | {elapsed:.1f}s]')
    return code


def _format_forecast_line(forecast: dict[str, Any], series_names: dict[str, str]) -> str:
    """Format a single forecast as a one-line summary."""
    fct_id: str = forecast.get('id', '?')
    series_id: str = forecast.get('series_id', '?')
    name = series_names.get(series_id, series_id)
    items: list[dict[str, Any]] = forecast.get('items', [])
    n_periods = len(items)

    if items:
        first_date = str(items[0].get('date', ''))[:7]
        last_date = str(items[-1].get('date', ''))[:7]
        date_str = f'{first_date} -> {last_date}'
    else:
        date_str = '-'

    return f'  {fct_id}  {name}  {n_periods} periods  {date_str}'


def print_forecast_result(result: StreamResult, elapsed: float) -> int:
    """Print final result for forecast command. Returns exit code.

    Progress and per-series events are already printed by on_forecast_event.
    This prints the summary table from ForecastingCompleted and the footer.
    """
    code = result.exit_code if result.exit_code is not None else 1

    if result.errors:
        for err in result.errors:
            print(f'error: {err}')
        print(f'[exit:{code} | {elapsed:.1f}s]')
        return code

    completed = result.get_event('ForecastingCompleted')
    if completed is not None:
        forecasts_data: list[dict[str, Any]] = completed['data'].get('forecasts', [])
        series_data: list[dict[str, Any]] = completed['data'].get('series', [])
        series_names = {s.get('id', ''): s.get('name', '') for s in series_data}

        for f in forecasts_data:
            print(_format_forecast_line(f, series_names))
    elif code != 0:
        if result.stderr.strip():
            print(f'error: {result.stderr.strip().splitlines()[-1]}')

    print(f'[exit:{code} | {elapsed:.1f}s]')
    return code
