"""Nexus — Step 1-5: Data Layer + Auth + Sync + Models Tests

D10: Data migration tests
A5: JWT auth flow tests
S6: Sync engine tests
"""

import pytest
from unittest.mock import AsyncMock, MagicMock


class TestDomainModels:
    """D10: Model integrity tests"""

    def test_platform_model_fields(self):
        """Platform model has all required fields"""
        from server.domain.models import Platform
        p = Platform(name="Linux", category="host", type="linux")
        assert p.name == "Linux"
        assert p.category == "host"
        assert p.type == "linux"
        # Column default only applies on DB INSERT, not Python construction

    def test_node_model_fields(self):
        """Node model has tree structure fields"""
        from server.domain.models import Node
        n = Node(name="Web Servers", parent_id=None, sort_order=1)
        assert n.name == "Web Servers"
        assert n.parent_id is None
        assert n.sort_order == 1

    def test_server_model_new_fields(self):
        """Server model has new extended fields"""
        from server.domain.models import Server
        s = Server(name="test", domain="1.2.3.4")
        assert s.platform_id is None
        assert s.node_id is None
        assert s.protocols is None
        assert s.extra_attrs is None
        # Column default only applies on DB INSERT, not Python construction

    def test_admin_model_jwt_fields(self):
        """Admin model has JWT fields"""
        from server.domain.models import Admin
        a = Admin(username="admin", password_hash="xxx")
        assert (a.token_version or 0) == 0

    def test_ssh_session_model(self):
        """SshSession model has lifecycle fields"""
        from server.domain.models import SshSession
        s = SshSession(server_id=1, admin_id=1)
        # Column default only applies on DB INSERT, not Python construction
        assert s.closed_at is None

    def test_command_log_model(self):
        """CommandLog model has required fields"""
        from server.domain.models import CommandLog
        c = CommandLog(server_id=1, command="ls -la")
        assert c.command == "ls -la"
        assert c.session_id is None  # Can be standalone


class TestAuthService:
    """A5: Auth service unit tests"""

    def test_jwt_token_creation(self):
        """JWT access token contains correct claims"""
        from server.application.services.auth_service import AuthService
        from server.domain.models import Admin

        service = AuthService(
            admin_repo=MagicMock(),
            attempt_repo=MagicMock(),
            audit_repo=MagicMock(),
        )
        admin = Admin(id=42, username="testuser", password_hash="xxx")
        token = service._create_access_token(admin)

        import jwt
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
        assert payload["sub"] == "42"
        assert payload["username"] == "testuser"
        assert "exp" in payload
        assert "iat" in payload

    def test_jwt_token_verification(self):
        """JWT token can be verified correctly"""
        from server.application.services.auth_service import AuthService
        from server.domain.models import Admin

        service = AuthService(
            admin_repo=MagicMock(),
            attempt_repo=MagicMock(),
            audit_repo=MagicMock(),
        )
        admin = Admin(id=42, username="testuser", password_hash="xxx")
        token = service._create_access_token(admin)

        payload = service._decode_access_token(token)
        assert payload is not None
        assert payload["sub"] == "42"

    def test_invalid_token_returns_none(self):
        """Invalid JWT token returns None"""
        from server.application.services.auth_service import AuthService
        service = AuthService(
            admin_repo=MagicMock(),
            attempt_repo=MagicMock(),
            audit_repo=MagicMock(),
        )
        result = service._decode_access_token("invalid.token.here")
        assert result is None

    def test_refresh_token_is_random(self):
        """Refresh tokens are random and unique"""
        from server.application.services.auth_service import AuthService
        service = AuthService(
            admin_repo=MagicMock(),
            attempt_repo=MagicMock(),
            audit_repo=MagicMock(),
        )
        from server.domain.models import Admin
        admin = Admin(id=1, username="admin", password_hash="xxx", token_version=0)
        t1 = service._create_refresh_token(admin)
        t2 = service._create_refresh_token(admin)
        assert t1 != t2
        assert len(t1) > 20


class TestTOTPVerification:
    """A5: TOTP verification tests"""

    def test_totp_invalid_code_rejected(self):
        """Invalid TOTP code is rejected"""
        from server.application.services.auth_service import AuthService
        service = AuthService(
            admin_repo=MagicMock(),
            attempt_repo=MagicMock(),
            audit_repo=MagicMock(),
        )
        # Generate a real secret and test with wrong code
        import base64, secrets
        secret = base64.b32encode(secrets.token_bytes(20)).decode()
        assert service._verify_totp("000000", secret) is False


class TestSyncEngineV2:
    """S6: Sync engine unit tests"""

    def test_max_concurrent_default(self):
        """Default concurrency is 10"""
        from server.application.services.sync_engine_v2 import MAX_CONCURRENT
        assert MAX_CONCURRENT == 10

    def test_engine_creation(self):
        """SyncEngineV2 can be instantiated with mock repos"""
        from server.application.services.sync_engine_v2 import SyncEngineV2
        engine = SyncEngineV2(
            server_repo=MagicMock(),
            sync_log_repo=MagicMock(),
            audit_repo=MagicMock(),
            retry_repo=MagicMock(),
        )
        assert engine is not None


from server.config import settings