"""Tests for the CLI submit flow — Chutes auth status check and token storage."""

from __future__ import annotations

import argparse
import json
import sys
import types
from http import HTTPStatus
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock, patch

import pytest

from oro_sdk.models.admission_status import AdmissionStatus
from oro_sdk.models.chutes_auth_status_response import ChutesAuthStatusResponse
from oro_sdk.models.submit_agent_response import SubmitAgentResponse
from oro_sdk.types import Response

# Stub bittensor_wallet so patch() works without the optional dependency.
if "bittensor_wallet" not in sys.modules:
    _bw = types.ModuleType("bittensor_wallet")
    _bw.Wallet = MagicMock  # type: ignore[attr-defined]
    sys.modules["bittensor_wallet"] = _bw

_OK_SUBMIT = Response(
    status_code=HTTPStatus.OK, content=b"{}", headers={},
    parsed=SubmitAgentResponse(
        admission_status=AdmissionStatus.ACCEPTED, hotkey="5Fake", message="OK",
    ),
)
_OK_STORE = Response(status_code=HTTPStatus.OK, content=b"{}", headers={}, parsed=None)
_ERR_STORE = Response(
    status_code=HTTPStatus.INTERNAL_SERVER_ERROR, headers={}, parsed=None,
    content=json.dumps({"detail": "storage failed"}).encode(),
)


@pytest.fixture()
def cli(tmp_path):
    """Patch all _submit dependencies with happy-path defaults. Returns (run, mocks).

    Call run() to invoke _submit. Override mocks.X.return_value before calling
    to test unhappy paths — only configure what the test cares about.
    """
    agent_file = tmp_path / "agent.py"
    agent_file.write_text("print('hello')")
    args = argparse.Namespace(
        agent_name="test-agent", agent_file=agent_file,
        wallet_name="default", wallet_hotkey="default",
        base_url="http://localhost:8000", chutes_token=None,
    )

    with (
        patch("bittensor_wallet.Wallet") as mock_wallet,
        patch("oro_sdk.bittensor_auth.BittensorAuthClient"),
        patch("oro_sdk.api.miner.get_chutes_auth_status.sync") as status,
        patch("oro_sdk.api.miner.store_chutes_token.sync_detailed") as store,
        patch("oro_sdk.api.miner.submit_agent.sync_detailed") as submit,
        patch("oro_sdk.chutes_auth.load_cached_tokens") as cache,
        patch("oro_sdk.chutes_auth.start_auth_flow") as auth_flow,
    ):
        w = MagicMock(); w.name = "default"; w.hotkey.ss58_address = "5Fake"
        mock_wallet.return_value = w

        # Happy-path defaults: token already stored, submission succeeds.
        status.return_value = ChutesAuthStatusResponse(connected=True)
        submit.return_value = _OK_SUBMIT

        from oro_sdk.cli import _submit
        m = SimpleNamespace(status=status, store=store, submit=submit,
                            cache=cache, auth_flow=auth_flow, args=args)
        yield lambda **kw: _submit(argparse.Namespace(**{**vars(args), **kw})), m


class TestChutesAuthFlow:
    def test_skips_auth_when_already_connected(self, cli, capsys):
        run, m = cli
        run()

        m.cache.assert_not_called()
        m.auth_flow.assert_not_called()
        m.store.assert_not_called()
        m.submit.assert_called_once()
        assert "already stored" in capsys.readouterr().out

    def test_stores_token_from_cli_flag(self, cli):
        run, m = cli
        m.status.return_value = ChutesAuthStatusResponse(connected=False)
        m.store.return_value = _OK_STORE

        run(chutes_token="explicit-token")

        m.cache.assert_not_called()
        m.auth_flow.assert_not_called()
        assert m.store.call_args.kwargs["body"].refresh_token == "explicit-token"

    def test_stores_token_from_cache(self, cli):
        run, m = cli
        m.status.return_value = ChutesAuthStatusResponse(connected=False)
        m.cache.return_value = {"refresh_token": "cached-token"}
        m.store.return_value = _OK_STORE

        run()

        m.auth_flow.assert_not_called()
        assert m.store.call_args.kwargs["body"].refresh_token == "cached-token"

    def test_runs_oauth_when_no_cache(self, cli):
        run, m = cli
        m.status.return_value = ChutesAuthStatusResponse(connected=False)
        m.cache.return_value = None
        m.auth_flow.return_value = {"refresh_token": "oauth-token"}
        m.store.return_value = _OK_STORE

        run()

        m.auth_flow.assert_called_once()
        assert m.store.call_args.kwargs["body"].refresh_token == "oauth-token"

    def test_exits_on_store_failure(self, cli):
        run, m = cli
        m.status.return_value = ChutesAuthStatusResponse(connected=False)
        m.cache.return_value = {"refresh_token": "tok"}
        m.store.return_value = _ERR_STORE

        with pytest.raises(SystemExit, match="1"):
            run()
        m.submit.assert_not_called()

    def test_no_inline_token_in_submit_body(self, cli):
        run, m = cli
        run()

        body = m.submit.call_args.kwargs["body"]
        assert "chutes_refresh_token" not in body.additional_properties
