from __future__ import annotations

import json
import os
from argparse import Namespace
from datetime import datetime, timedelta, timezone
from pathlib import Path

import pytest

from mikoshi import auth
from mikoshi.auth import (
    AuthError,
    AuthState,
    _ensure_state_match,
    _parse_paste_payload,
    config_path,
    load_auth_state,
    save_auth_state,
    save_broker_config,
)
from mikoshi.cli import cmd_logout, cmd_whoami


def _set_index_root(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("MIKOSHI_INDEX_ROOT", str(tmp_path))


def test_auth_roundtrip(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    _set_index_root(tmp_path, monkeypatch)
    state = AuthState(
        access_token="abc",
        expires_at="2026-03-01T00:00:00Z",
        plan="free",
        features=["local_index", "mcp"],
    )
    save_auth_state(state)
    loaded = load_auth_state()
    assert loaded == state


def test_auth_permissions_enforced(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    _set_index_root(tmp_path, monkeypatch)
    state = AuthState(
        access_token="abc",
        expires_at="2026-03-01T00:00:00Z",
        plan="free",
        features=["local_index"],
    )
    save_auth_state(state)
    path = auth.auth_path()
    mode = path.stat().st_mode & 0o777
    assert mode == 0o600

    def _raise_chmod(_path: str, _mode: int) -> None:
        raise PermissionError("chmod failed")

    monkeypatch.setattr(os, "chmod", _raise_chmod)
    with pytest.raises(PermissionError):
        save_auth_state(state)


def test_auth_file_contains_minimum_fields(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    _set_index_root(tmp_path, monkeypatch)
    state = AuthState(
        access_token="token",
        expires_at="2026-03-01T00:00:00Z",
        plan="free",
        features=["local_index"],
    )
    save_auth_state(state)
    content = auth.auth_path().read_text(encoding="utf-8")
    assert "refresh_token" not in content
    assert "api_base_url" not in content
    assert "publishable_key" not in content


def test_config_roundtrip(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    _set_index_root(tmp_path, monkeypatch)
    save_broker_config("https://neet.gg")
    path = config_path()
    mode = path.stat().st_mode & 0o777
    assert mode == 0o600
    data = json.loads(path.read_text(encoding="utf-8"))
    assert list(data.keys()) == ["api_base_url"]
    assert data["api_base_url"] == "https://neet.gg"


def test_invalid_json_rejects() -> None:
    with pytest.raises(AuthError):
        _parse_paste_payload("not-json")


def test_state_mismatch_rejects() -> None:
    with pytest.raises(AuthError):
        _ensure_state_match("expected", "actual")


def test_whoami_expired_token(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
    capsys: pytest.CaptureFixture[str],
) -> None:
    _set_index_root(tmp_path, monkeypatch)
    expired = datetime.now(timezone.utc) - timedelta(days=1)
    state = AuthState(
        access_token="abc",
        expires_at=expired.isoformat().replace("+00:00", "Z"),
        plan="free",
        features=["local_index"],
    )
    save_auth_state(state)
    code = cmd_whoami(Namespace())
    output = capsys.readouterr().out.strip()
    assert output == "🔒 Not signed in"
    assert code == 1


def test_logout_removes_auth_file(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
    capsys: pytest.CaptureFixture[str],
) -> None:
    _set_index_root(tmp_path, monkeypatch)
    state = AuthState(
        access_token="abc",
        expires_at="2026-03-01T00:00:00Z",
        plan="free",
        features=["local_index"],
    )
    save_auth_state(state)
    assert auth.auth_path().exists()
    code = cmd_logout(Namespace())
    output = capsys.readouterr().out.strip()
    assert output == "✅ Logged out"
    assert code == 0
    assert not auth.auth_path().exists()
