"""ansel forecast — generate demand forecasts."""

from __future__ import annotations

import sys
import time
from pathlib import Path
from typing import cast

from ..auth import get_token
from ..client import get_json, post_resume, post_stream
from ..output import on_forecast_event, print_forecast_result
from ..parse import JSONObject
from . import resolve_csv_files


def _require_token() -> str | None:
    """Get auth token or print error."""
    token = get_token()
    if not token:
        print(
            'error: no credentials found. Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN.',
            file=sys.stderr,
        )
    return token


def _parse_flags(args: list[str]) -> tuple[list[str], dict[str, str]]:
    """Separate positional args from --flag value pairs.

    Returns (positional_args, flags_dict).
    """
    positional: list[str] = []
    flags: dict[str, str] = {}
    i = 0
    while i < len(args):
        if args[i].startswith('--') and i + 1 < len(args):
            flags[args[i][2:]] = args[i + 1]
            i += 2
        else:
            positional.append(args[i])
            i += 1
    return positional, flags


def _build_params(flags: dict[str, str]) -> dict[str, str]:
    """Build query params from parsed flags."""
    params: dict[str, str] = {}
    for key in ('horizon', 'interval'):
        if key in flags:
            params[key] = flags[key]
    return params


def _str(val: object, default: str = '?') -> str:
    """Coerce a JSONObject value to str, with a fallback default."""
    if val is None:
        return default
    return str(val)


def _run_forecast_path(token: str, path: Path, params: dict[str, str]) -> int:
    """ansel forecast --data <path> — upload CSVs and run full pipeline."""
    csv_files = resolve_csv_files(path)
    if csv_files is None:
        return 1

    files_data = [('file', csv_file.name, csv_file.read_text()) for csv_file in csv_files]
    start = time.monotonic()
    result = post_stream(
        '/forecasts', token, files=files_data, params=params, on_event=on_forecast_event
    )
    elapsed = time.monotonic() - start
    return print_forecast_result(result, elapsed)


def _run_forecast_ids(token: str, file_ids: list[str], params: dict[str, str]) -> int:
    """ansel forecast <file_id...> — run pipeline from pre-uploaded files."""
    start = time.monotonic()
    result = post_stream(
        '/forecasts',
        token,
        json_body={'file_ids': file_ids},
        params=params,
        on_event=on_forecast_event,
    )
    elapsed = time.monotonic() - start
    return print_forecast_result(result, elapsed)


def _run_resume(token: str, run_id: str) -> int:
    """ansel forecast <run_id> — resume from a prior run."""
    start = time.monotonic()
    result = post_resume('forecasts', run_id, token, on_event=on_forecast_event)
    elapsed = time.monotonic() - start
    return print_forecast_result(result, elapsed)


def _run_get(token: str, forecast_id: str) -> int:
    """ansel forecast get <fct_id>"""
    import json

    data = get_json(token, f'/forecasts/{forecast_id}')
    if data is None:
        print(f'error: forecast not found: {forecast_id}', file=sys.stderr)
        return 1

    series_id: str = _str(data.get('series_id'))
    series_name: str = _str(data.get('series_name'), '')
    series_str = f'{series_name} ({series_id})' if series_name else series_id
    model_id: str = _str(data.get('model_id'))
    interval: str = _str(data.get('interval'), '')
    horizon: str = _str(data.get('horizon'), '')
    created_at = str(data.get('created_at', ''))[:16]

    horizon_str = f'{horizon} {interval}s' if horizon and interval else ''
    parts = [forecast_id, series_str, horizon_str, created_at]
    print('  '.join(p for p in parts if p))
    print(f'model: https://api.ansel.sh/models/{model_id}')
    print()

    raw_items = data.get('items', '[]')
    items: list[JSONObject]
    if isinstance(raw_items, str):
        try:
            parsed: object = json.loads(raw_items)  # pyright: ignore[reportAny]
            items = (
                [cast(JSONObject, i) for i in cast(list[object], parsed) if isinstance(i, dict)]
                if isinstance(parsed, list)
                else []
            )
        except (json.JSONDecodeError, TypeError):
            items = []
    elif isinstance(raw_items, list):
        items = [cast(JSONObject, i) for i in cast(list[object], raw_items) if isinstance(i, dict)]
    else:
        items = []

    if items:
        print('date        value')
        for item in items:
            print(f'{_str(item.get("date"))}  {_str(item.get("value"))}')

    return 0


def _run_list(token: str) -> int:
    """ansel forecast list"""
    from ..output import format_forecast_line

    data = get_json(token, '/forecasts')
    if data is None:
        print('error: failed to fetch forecasts', file=sys.stderr)
        return 1

    raw_forecasts = data.get('forecasts', [])
    forecasts_list: list[JSONObject] = (
        [cast(JSONObject, f) for f in cast(list[object], raw_forecasts) if isinstance(f, dict)]
        if isinstance(raw_forecasts, list)
        else []
    )
    if not forecasts_list:
        print('no forecasts found')
        return 0

    for f in forecasts_list:
        print(format_forecast_line(f))
    return 0


def run(args: list[str]) -> int:
    """Execute the forecast command."""
    if not args:
        print('forecast: forecast demand from CSV data')
        print()
        print('  forecast                       Full pipeline from CSVs in current directory')
        print('  forecast --data <path>         Full pipeline from CSV file or directory')
        print('  forecast <fil_id...>           Pipeline from pre-uploaded files')
        print('  forecast list                  List generated forecasts')
        print('  forecast get <fct_id>          Get forecast details and items')
        print()
        print('Options:')
        print('  --data PATH       CSV file or directory (default: .)')
        print('  --horizon N       Periods to forecast (default: 12)')
        print('  --interval STR    week | month | quarter (default: month)')
        return 1

    positional, flags = _parse_flags(args)
    params = _build_params(flags)

    token = _require_token()
    if not token:
        return 1

    # Subcommands
    if positional and positional[0] == 'get':
        if len(positional) < 2:
            print('error: usage: forecast get <fct_id>', file=sys.stderr)
            return 1
        return _run_get(token, positional[1])

    if positional and positional[0] == 'list':
        return _run_list(token)

    # Positional IDs: run_ prefix → resume, fil_ prefix → pre-uploaded files
    if positional:
        if len(positional) == 1 and positional[0].startswith('run_'):
            return _run_resume(token, positional[0])

        if all(arg.startswith('fil_') for arg in positional):
            return _run_forecast_ids(token, positional, params)

        print(f"error: unknown argument '{positional[0]}'", file=sys.stderr)
        return 1

    # --data flag or default to cwd
    path = Path(flags.get('data', '.'))
    if not path.exists():
        print(f'error: {path} not found', file=sys.stderr)
        return 1
    return _run_forecast_path(token, path, params)
