"""Unit tests for Subsonic crypto + id helpers (no DB, no network)."""

import hashlib
import uuid

import pytest
from app.api.rest import ids
from app.api.rest.ids import IdKind
from app.core.security import SubsonicPasswordCipher, generate_subsonic_password
from app.domain.errors import AuthenticationError, NotFoundError


def test_generate_subsonic_password_is_long_and_unique() -> None:
    a = generate_subsonic_password()
    b = generate_subsonic_password()
    assert a != b
    assert len(a) >= 20


def test_cipher_roundtrip() -> None:
    cipher = SubsonicPasswordCipher("a-secret-key")
    plaintext = generate_subsonic_password()
    token = cipher.encrypt(plaintext)
    assert token != plaintext
    assert cipher.decrypt(token) == plaintext


def test_cipher_token_then_md5_matches() -> None:
    """The decrypted app-password must reproduce a client's t=md5(password+salt)."""
    cipher = SubsonicPasswordCipher("a-secret-key")
    password = generate_subsonic_password()
    enc = cipher.encrypt(password)

    salt = "c19b2d"
    decrypted = cipher.decrypt(enc)
    expected = hashlib.md5((decrypted + salt).encode(), usedforsecurity=False).hexdigest()
    client_token = hashlib.md5((password + salt).encode(), usedforsecurity=False).hexdigest()
    assert expected == client_token


def test_cipher_wrong_key_fails() -> None:
    token = SubsonicPasswordCipher("key-one").encrypt("hunter2")
    with pytest.raises(AuthenticationError):
        SubsonicPasswordCipher("key-two").decrypt(token)


def test_id_encode_decode_roundtrip() -> None:
    value = uuid.uuid4()
    assert ids.decode_track(ids.encode_track(value)) == value
    assert ids.decode_album(ids.encode_album(value)) == value
    assert ids.decode_artist(ids.encode_artist(value)) == value
    assert ids.decode_playlist(ids.encode_playlist(value)) == value


def test_id_parse_returns_kind() -> None:
    value = uuid.uuid4()
    kind, parsed = ids.parse(ids.encode_album(value))
    assert kind is IdKind.ALBUM
    assert parsed == value


def test_id_wrong_prefix_rejected() -> None:
    track = ids.encode_track(uuid.uuid4())
    with pytest.raises(NotFoundError):
        ids.decode_album(track)


def test_id_malformed_rejected() -> None:
    with pytest.raises(NotFoundError):
        ids.parse("not-a-real-id")
