"""Tests for Chutes OAuth 2.0 + PKCE authentication utilities."""

import base64
import hashlib
import json
import os
import stat
from pathlib import Path
from urllib.parse import parse_qs, urlparse

import pytest

from oro_sdk.chutes_auth import (
    _build_authorize_url,
    generate_pkce_pair,
    load_cached_tokens,
    save_cached_tokens,
)


class TestGeneratePkcePair:

    def test_returns_verifier_and_challenge(self):
        verifier, challenge = generate_pkce_pair()
        assert isinstance(verifier, str)
        assert isinstance(challenge, str)
        assert len(verifier) > 40

    def test_challenge_is_sha256_of_verifier(self):
        verifier, challenge = generate_pkce_pair()
        expected = base64.urlsafe_b64encode(
            hashlib.sha256(verifier.encode()).digest()
        ).rstrip(b"=").decode()
        assert challenge == expected

    def test_pairs_are_unique(self):
        pair1 = generate_pkce_pair()
        pair2 = generate_pkce_pair()
        assert pair1[0] != pair2[0]
        assert pair1[1] != pair2[1]


class TestBuildAuthorizeUrl:

    def test_includes_all_required_params(self):
        url = _build_authorize_url(
            client_id="test-client",
            redirect_uri="http://localhost:8765/callback",
            code_challenge="test-challenge",
            state="test-state",
        )
        parsed = urlparse(url)
        params = parse_qs(parsed.query)

        assert params["response_type"] == ["code"]
        assert params["client_id"] == ["test-client"]
        assert params["redirect_uri"] == ["http://localhost:8765/callback"]
        assert params["code_challenge"] == ["test-challenge"]
        assert params["code_challenge_method"] == ["S256"]
        assert params["state"] == ["test-state"]
        assert params["scope"] == ["chutes:invoke"]

    def test_uses_custom_authorize_url(self):
        url = _build_authorize_url(
            client_id="c",
            redirect_uri="http://localhost/cb",
            code_challenge="ch",
            state="s",
            authorize_url="https://custom.example.com/auth",
        )
        assert url.startswith("https://custom.example.com/auth?")


class TestTokenCache:

    def test_load_returns_none_for_missing_file(self, tmp_path):
        assert load_cached_tokens(tmp_path / "nonexistent.json") is None

    def test_load_returns_none_for_invalid_json(self, tmp_path):
        bad = tmp_path / "bad.json"
        bad.write_text("not json")
        assert load_cached_tokens(bad) is None

    def test_load_returns_none_without_refresh_token(self, tmp_path):
        f = tmp_path / "tokens.json"
        f.write_text(json.dumps({"access_token": "abc"}))
        assert load_cached_tokens(f) is None

    def test_save_and_load_round_trip(self, tmp_path):
        path = tmp_path / "sub" / "tokens.json"
        tokens = {"access_token": "a", "refresh_token": "r"}
        save_cached_tokens(tokens, path)

        loaded = load_cached_tokens(path)
        assert loaded == tokens

    def test_save_creates_parent_dir(self, tmp_path):
        path = tmp_path / "new_dir" / "tokens.json"
        save_cached_tokens({"refresh_token": "r"}, path)
        assert path.exists()

    def test_save_sets_restricted_permissions(self, tmp_path):
        path = tmp_path / "tokens.json"
        save_cached_tokens({"refresh_token": "r"}, path)

        file_mode = stat.S_IMODE(path.stat().st_mode)
        assert file_mode == 0o600

        dir_mode = stat.S_IMODE(path.parent.stat().st_mode)
        assert dir_mode == 0o700
