"""tyta electric subcommands: status, refresh, charge-now, reserve-charge, set-charging-time."""

from __future__ import annotations

import asyncio
import json
import sys
from typing import Annotated

import typer

from tyta.cli import render
from tyta.core import auth as core_auth
from tyta.core import config as core_config
from tyta.core import electric as core_electric
from tyta.core import envelope as env
from tyta.core import vehicles as core_vehicles
from tyta.core.client import ToyotaApiError, ToyotaClient

app = typer.Typer(
    name="electric",
    help="Electric vehicle commands.",
    no_args_is_help=True,
)


def _get_state() -> dict:
    from tyta.cli.main import _get_state as _gs

    return _gs()


def _is_json_mode() -> bool:
    return _get_state().get("json", False)


def _load_config() -> dict:
    config_path = core_config.get_config_path(_get_state().get("config_file"))
    try:
        return core_config.load_config(config_path)
    except Exception:  # noqa: BLE001
        return {}


def _emit_and_exit(data: dict, exit_code: int = 0, command: str = "") -> None:
    json_mode = _is_json_mode()
    if exit_code != 0:
        render.render_error(data, json_mode)
    else:
        render.render_output(data, json_mode, command, config=_load_config())
    sys.exit(exit_code)


async def _build_client_and_vin(
    vin_flag: str | None,
    state: dict,
) -> tuple[ToyotaClient, str, list[str]] | None:
    """Resolve auth, build client, and resolve VIN. Returns None and emits error on failure."""
    debug = state.get("debug", False)
    auth_path = core_auth.get_auth_path(state.get("auth_file"))
    config_path = state.get("config_file")

    try:
        token_data = await core_auth.get_valid_token(auth_path)
    except core_auth.NotAuthenticatedError as exc:
        _emit_and_exit(env.error(exc.code, exc.message), exit_code=1)
        return None
    except core_auth.TokenExpiredError as exc:
        _emit_and_exit(env.error(exc.code, exc.message), exit_code=1)
        return None
    except json.JSONDecodeError:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("invalid_config", "Token file is malformed JSON."), exit_code=1)
        return None

    client = ToyotaClient(
        access_token=token_data["access_token"],
        uuid=token_data["uuid"],
    )

    try:
        vin, warnings = await core_vehicles.resolve_vin(
            client, vin_flag=vin_flag, config_path=config_path
        )
    except core_vehicles.NoVehiclesError as exc:
        _emit_and_exit(env.error(exc.code, exc.message), exit_code=1)
        return None
    except core_vehicles.VehicleNotFoundError as exc:
        _emit_and_exit(env.error(exc.code, exc.message), exit_code=1)
        return None
    except ToyotaApiError as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("api_error", f"Toyota API error: {exc.message}"), exit_code=1)
        return None

    return client, vin, warnings


@app.command("status")
def electric_status(
    vin: Annotated[
        str | None,
        typer.Option("--vin", help="VIN of the vehicle to query.", show_default=False),
    ] = None,
) -> None:
    """Show the current electric/battery status of a vehicle."""
    asyncio.run(_electric_status(vin))


async def _electric_status(vin_flag: str | None) -> None:
    state = _get_state()
    debug = state.get("debug", False)
    result = await _build_client_and_vin(vin_flag, state)
    if result is None:
        return
    client, vin, warnings = result

    try:
        data = await core_electric.get_electric_status(client, vin)
    except ToyotaApiError as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("api_error", f"Toyota API error: {exc.message}"), exit_code=1)
        return
    except Exception as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("unknown_error", str(exc)), exit_code=1)
        return

    _emit_and_exit(
        env.ok({"vin": vin, "electric_status": data}, warnings=warnings),
        command="electric status",
    )


@app.command("refresh")
def electric_refresh(
    vin: Annotated[
        str | None,
        typer.Option("--vin", help="VIN of the vehicle to query.", show_default=False),
    ] = None,
) -> None:
    """Request a real-time EV status refresh from the vehicle (drains 12V battery)."""
    asyncio.run(_electric_refresh(vin))


async def _electric_refresh(vin_flag: str | None) -> None:
    state = _get_state()
    debug = state.get("debug", False)
    result = await _build_client_and_vin(vin_flag, state)
    if result is None:
        return
    client, vin, warnings = result

    try:
        data = await core_electric.refresh_electric_realtime_status(client, vin)
    except ToyotaApiError as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("api_error", f"Toyota API error: {exc.message}"), exit_code=1)
        return
    except Exception as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("unknown_error", str(exc)), exit_code=1)
        return

    # Always include the 12V warning — regardless of other warnings
    all_warnings = list(warnings) + [core_electric.EV_12V_WARNING]
    json_mode = _is_json_mode()
    if not json_mode:
        # In human mode, delegate entirely to the renderer (which emits the 12V warning panel)
        render.render_output(
            env.ok({"vin": vin, **data}, warnings=warnings),
            json_mode,
            "electric refresh",
            config=_load_config(),
        )
        sys.exit(0)
    else:
        _emit_and_exit(
            env.ok({"vin": vin, **data}, warnings=all_warnings), command="electric refresh"
        )


@app.command("charge-now")
def electric_charge_now(
    vin: Annotated[
        str | None,
        typer.Option("--vin", help="VIN of the vehicle to query.", show_default=False),
    ] = None,
) -> None:
    """Send an immediate charge start command to the vehicle."""
    asyncio.run(_electric_charge_now(vin))


async def _electric_charge_now(vin_flag: str | None) -> None:
    state = _get_state()
    debug = state.get("debug", False)
    result = await _build_client_and_vin(vin_flag, state)
    if result is None:
        return
    client, vin, warnings = result

    try:
        data = await core_electric.send_charge_now(client, vin)
    except ToyotaApiError as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("api_error", f"Toyota API error: {exc.message}"), exit_code=1)
        return
    except Exception as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("unknown_error", str(exc)), exit_code=1)
        return

    _emit_and_exit(env.ok({"vin": vin, **data}, warnings=warnings), command="electric charge-now")


@app.command("reserve-charge")
def electric_reserve_charge(
    day: Annotated[
        str,
        typer.Option("--day", help="Uppercase weekday name (e.g. MONDAY, TUESDAY)."),
    ],
    start: Annotated[
        str,
        typer.Option("--start", help="Start time in HH:MM 24h format."),
    ],
    end: Annotated[
        str | None,
        typer.Option("--end", help="End time in HH:MM 24h format (required for startEnd)."),
    ] = None,
    charge_type: Annotated[
        str,
        typer.Option(
            "--charge-type",
            help="Charge type: startOnly or startEnd. Defaults to startOnly.",
        ),
    ] = "startOnly",
    vin: Annotated[
        str | None,
        typer.Option("--vin", help="VIN of the vehicle to query.", show_default=False),
    ] = None,
) -> None:
    """Reserve a charging schedule on the vehicle."""
    asyncio.run(_electric_reserve_charge(vin, day, start, end, charge_type))


async def _electric_reserve_charge(
    vin_flag: str | None,
    day: str,
    start: str,
    end: str | None,
    charge_type: str,
) -> None:
    state = _get_state()
    debug = state.get("debug", False)

    # Validate: startEnd charge_type requires --end
    if end is not None and charge_type == "startOnly":
        charge_type = "startEnd"

    result = await _build_client_and_vin(vin_flag, state)
    if result is None:
        return
    client, vin, warnings = result

    try:
        data = await core_electric.send_reserve_charge(client, vin, day, start, end, charge_type)
    except ValueError as exc:
        _emit_and_exit(env.error("invalid_input", str(exc)), exit_code=1)
        return
    except ToyotaApiError as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("api_error", f"Toyota API error: {exc.message}"), exit_code=1)
        return
    except Exception as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("unknown_error", str(exc)), exit_code=1)
        return

    _emit_and_exit(
        env.ok({"vin": vin, **data}, warnings=warnings), command="electric reserve-charge"
    )


@app.command("set-charging-time")
def electric_set_charging_time(
    day: Annotated[
        str,
        typer.Option("--day", help="Uppercase weekday name (e.g. MONDAY, TUESDAY)."),
    ],
    start: Annotated[
        str,
        typer.Option("--start", help="Start time in HH:MM 24h format."),
    ],
    end: Annotated[
        str | None,
        typer.Option("--end", help="End time in HH:MM 24h format (required for startEnd)."),
    ] = None,
    charge_type: Annotated[
        str,
        typer.Option(
            "--charge-type",
            help="Charge type: startOnly or startEnd. Defaults to startOnly.",
        ),
    ] = "startOnly",
    vin: Annotated[
        str | None,
        typer.Option("--vin", help="VIN of the vehicle to query.", show_default=False),
    ] = None,
) -> None:
    """Set the charging time schedule on the vehicle."""
    asyncio.run(_electric_set_charging_time(vin, day, start, end, charge_type))


async def _electric_set_charging_time(
    vin_flag: str | None,
    day: str,
    start: str,
    end: str | None,
    charge_type: str,
) -> None:
    state = _get_state()
    debug = state.get("debug", False)

    # Validate: startEnd charge_type requires --end
    if end is not None and charge_type == "startOnly":
        charge_type = "startEnd"

    result = await _build_client_and_vin(vin_flag, state)
    if result is None:
        return
    client, vin, warnings = result

    try:
        data = await core_electric.send_set_charging_time(client, vin, day, start, end, charge_type)
    except ValueError as exc:
        _emit_and_exit(env.error("invalid_input", str(exc)), exit_code=1)
        return
    except ToyotaApiError as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("api_error", f"Toyota API error: {exc.message}"), exit_code=1)
        return
    except Exception as exc:
        if debug:
            import traceback

            traceback.print_exc(file=sys.stderr)
        _emit_and_exit(env.error("unknown_error", str(exc)), exit_code=1)
        return

    _emit_and_exit(
        env.ok({"vin": vin, **data}, warnings=warnings), command="electric set-charging-time"
    )
