# -*- coding: utf-8 -*-
"""Tests for local auth service."""
from __future__ import annotations

from datetime import timedelta

import pytest

from copaw.auth import service
from copaw.config.config import Config


def _patch_auth_storage(monkeypatch, tmp_path):
    users_path = tmp_path / "auth" / "users.json"
    sessions_path = tmp_path / "auth" / "sessions.json"
    monkeypatch.setattr(service, "get_auth_users_path", lambda: users_path)
    monkeypatch.setattr(
        service,
        "get_auth_sessions_path",
        lambda: sessions_path,
    )

    cfg_holder = {"cfg": Config()}

    def _load_config():
        return cfg_holder["cfg"]

    def _save_config(cfg):
        cfg_holder["cfg"] = cfg

    monkeypatch.setattr(service, "load_config", _load_config)
    monkeypatch.setattr(service, "save_config", _save_config)
    return cfg_holder


def test_register_once_and_verify_credentials(monkeypatch, tmp_path):
    _patch_auth_storage(monkeypatch, tmp_path)

    username = service.register_first_user(
        "alice",
        "password123",
        display_name="Alice",
        avatar_data_url="data:image/png;base64,AAAA",
    )
    assert username == "alice"
    assert service.is_user_registered()
    assert service.verify_user_credentials("alice", "password123")
    assert not service.verify_user_credentials("alice", "wrong")
    user = service.get_registered_user()
    assert user is not None
    assert user.display_name == "Alice"
    assert user.avatar_data_url == "data:image/png;base64,AAAA"

    with pytest.raises(service.AuthError) as exc:
        service.register_first_user("bob", "password123")
    assert exc.value.code == "already_registered"


def test_session_auth_and_expiry(monkeypatch, tmp_path):
    _patch_auth_storage(monkeypatch, tmp_path)
    service.register_first_user("alice", "password123")

    token, expires_at = service.create_session_for_user("alice", ttl_days=1)
    user = service.authenticate_session(token)
    assert user is not None
    assert user.username == "alice"

    monkeypatch.setattr(
        service,
        "_now_utc",
        lambda: expires_at + timedelta(seconds=1),
    )
    assert service.authenticate_session(token) is None


def test_change_password_rotates_sessions(monkeypatch, tmp_path):
    _patch_auth_storage(monkeypatch, tmp_path)
    service.register_first_user("alice", "password123")
    token, _ = service.create_session_for_user("alice", ttl_days=1)
    assert service.authenticate_session(token) is not None

    service.change_password(
        username="alice",
        old_password="password123",
        new_password="newpass123",
    )

    assert not service.verify_user_credentials("alice", "password123")
    assert service.verify_user_credentials("alice", "newpass123")
    assert service.authenticate_session(token) is None


def test_change_password_rejects_wrong_old_password(monkeypatch, tmp_path):
    _patch_auth_storage(monkeypatch, tmp_path)
    service.register_first_user("alice", "password123")

    with pytest.raises(service.AuthError) as exc:
        service.change_password(
            username="alice",
            old_password="wrong-password",
            new_password="newpass123",
        )

    assert exc.value.code == "invalid_credentials"
    assert service.verify_user_credentials("alice", "password123")
    assert not service.verify_user_credentials("alice", "newpass123")


def test_delete_account_requires_correct_password(monkeypatch, tmp_path):
    _patch_auth_storage(monkeypatch, tmp_path)
    service.register_first_user("alice", "password123")
    token, _ = service.create_session_for_user("alice", ttl_days=1)

    with pytest.raises(service.AuthError) as exc:
        service.delete_account(username="alice", password="wrong-password")
    assert exc.value.code == "invalid_credentials"
    assert service.is_user_registered()
    assert service.authenticate_session(token) is not None

    service.delete_account(username="alice", password="password123")
    assert not service.is_user_registered()
    assert service.authenticate_session(token) is None


def test_revoke_session(monkeypatch, tmp_path):
    _patch_auth_storage(monkeypatch, tmp_path)
    service.register_first_user("alice", "password123")
    token, _ = service.create_session_for_user("alice", ttl_days=1)
    assert service.authenticate_session(token) is not None
    assert service.revoke_session(token)
    assert service.authenticate_session(token) is None


def test_session_ttl_days_configurable(monkeypatch, tmp_path):
    _patch_auth_storage(monkeypatch, tmp_path)

    assert service.get_session_ttl_days() == 7
    assert service.set_session_ttl_days(30) == 30
    assert service.get_session_ttl_days() == 30

    with pytest.raises(service.AuthError) as exc:
        service.set_session_ttl_days(0)
    assert exc.value.code == "invalid_ttl"
