# #region Test.Schemas.Profile [C:2] [TYPE Module] [SEMANTICS test,schema,profile]
# @BRIEF Tests for schemas/profile.py — ProfilePermissionState, ProfileSecuritySummary,
#        ProfilePreference, ProfilePreferenceUpdateRequest, ProfilePreferenceResponse,
#        SupersetAccountLookupRequest, SupersetAccountCandidate, SupersetAccountLookupResponse.
# @RELATION BINDS_TO -> [ProfileSchemas]
# @TEST_EDGE: missing_field -> optional fields default correctly
# @TEST_EDGE: invalid_type -> field constraints (ge/le) reject bad values
# @TEST_EDGE: serialization -> round-trip via model_dump/model_validate

from datetime import datetime, timezone
import pytest
from pydantic import ValidationError

from src.schemas.profile import (
    ProfilePermissionState,
    ProfilePreference,
    ProfilePreferenceResponse,
    ProfilePreferenceUpdateRequest,
    ProfileSecuritySummary,
    SupersetAccountCandidate,
    SupersetAccountLookupRequest,
    SupersetAccountLookupResponse,
)


class TestProfilePermissionState:
    """ProfilePermissionState — key and allowed flag."""

    def test_minimal(self):
        p = ProfilePermissionState(key="dashboard", allowed=True)
        assert p.key == "dashboard"
        assert p.allowed is True

    def test_not_allowed(self):
        p = ProfilePermissionState(key="dataset", allowed=False)
        assert p.allowed is False

    def test_serialize_roundtrip(self):
        p = ProfilePermissionState(key="dashboard:write", allowed=False)
        data = p.model_dump()
        restored = ProfilePermissionState.model_validate(data)
        assert restored.key == "dashboard:write"
        assert restored.allowed is False


class TestProfileSecuritySummary:
    """ProfileSecuritySummary — security snapshot defaults."""

    def test_defaults(self):
        s = ProfileSecuritySummary()
        assert s.read_only is True
        assert s.auth_source is None
        assert s.current_role is None
        assert s.role_source is None
        assert s.roles == []
        assert s.permissions == []

    def test_with_values(self):
        perms = [ProfilePermissionState(key="dashboard", allowed=True)]
        s = ProfileSecuritySummary(
            read_only=False, auth_source="ADFS", current_role="Admin",
            role_source="ADFS", roles=["Admin"], permissions=perms,
        )
        assert s.read_only is False
        assert s.current_role == "Admin"
        assert len(s.permissions) == 1

    def test_serialize_roundtrip(self):
        s = ProfileSecuritySummary(auth_source="LOCAL", roles=["Viewer"])
        data = s.model_dump()
        restored = ProfileSecuritySummary.model_validate(data)
        assert restored.auth_source == "LOCAL"
        assert "Viewer" in restored.roles


class TestProfilePreference:
    """ProfilePreference — full preference model with defaults."""

    def test_minimal(self):
        ts = datetime.now(timezone.utc)
        p = ProfilePreference(user_id="u-1", created_at=ts, updated_at=ts)
        assert p.user_id == "u-1"
        assert p.superset_username is None
        assert p.superset_username_normalized is None
        assert p.show_only_my_dashboards is False
        assert p.show_only_slug_dashboards is True
        assert p.start_page == "dashboards"
        assert p.auto_open_task_drawer is True
        assert p.dashboards_table_density == "comfortable"
        assert p.notify_on_fail is True
        assert p.has_git_personal_access_token is False

    def test_with_all_fields(self):
        ts = datetime.now(timezone.utc)
        p = ProfilePreference(
            user_id="u-1",
            superset_username="jdoe",
            superset_username_normalized="jdoe",
            show_only_my_dashboards=True,
            show_only_slug_dashboards=False,
            git_username="jdoe",
            git_email="j@d.com",
            has_git_personal_access_token=True,
            git_personal_access_token_masked="jd***oe",
            start_page="datasets",
            auto_open_task_drawer=False,
            dashboards_table_density="compact",
            telegram_id="@jdoe",
            email_address="j@d.com",
            notify_on_fail=False,
            created_at=ts,
            updated_at=ts,
        )
        assert p.show_only_my_dashboards is True
        assert p.git_username == "jdoe"
        assert p.start_page == "datasets"
        assert p.dashboards_table_density == "compact"
        assert p.notify_on_fail is False

    def test_serialize_roundtrip(self):
        ts = datetime.now(timezone.utc)
        p = ProfilePreference(user_id="u-1", created_at=ts, updated_at=ts)
        data = p.model_dump()
        restored = ProfilePreference.model_validate(data)
        assert restored.user_id == "u-1"
        assert restored.start_page == "dashboards"

    def test_from_attributes(self):
        class FakeORM:
            user_id = "u-1"
            superset_username = "admin"
            superset_username_normalized = "admin"
            show_only_my_dashboards = False
            show_only_slug_dashboards = True
            git_username = None
            git_email = None
            has_git_personal_access_token = False
            git_personal_access_token_masked = None
            start_page = "dashboards"
            auto_open_task_drawer = True
            dashboards_table_density = "comfortable"
            telegram_id = None
            email_address = None
            notify_on_fail = True
            created_at = datetime.now(timezone.utc)
            updated_at = datetime.now(timezone.utc)
        p = ProfilePreference.model_validate(FakeORM())
        assert p.user_id == "u-1"


class TestProfilePreferenceUpdateRequest:
    """ProfilePreferenceUpdateRequest — all fields optional."""

    def test_defaults(self):
        req = ProfilePreferenceUpdateRequest()
        assert req.superset_username is None
        assert req.show_only_my_dashboards is None
        assert req.show_only_slug_dashboards is None
        assert req.git_username is None
        assert req.git_email is None
        assert req.git_personal_access_token is None
        assert req.start_page is None
        assert req.auto_open_task_drawer is None
        assert req.dashboards_table_density is None
        assert req.telegram_id is None
        assert req.email_address is None
        assert req.notify_on_fail is None

    def test_partial_update(self):
        req = ProfilePreferenceUpdateRequest(superset_username="jdoe", show_only_my_dashboards=True)
        assert req.superset_username == "jdoe"
        assert req.show_only_my_dashboards is True

    def test_start_page_literals(self):
        for page in ["dashboards", "datasets", "reports", "reports-logs"]:
            req = ProfilePreferenceUpdateRequest(start_page=page)
            assert req.start_page == page

    def test_invalid_start_page_raises(self):
        with pytest.raises(ValidationError):
            ProfilePreferenceUpdateRequest(start_page="invalid")

    def test_density_literals(self):
        for density in ["compact", "comfortable", "free"]:
            req = ProfilePreferenceUpdateRequest(dashboards_table_density=density)
            assert req.dashboards_table_density == density

    def test_invalid_density_raises(self):
        with pytest.raises(ValidationError):
            ProfilePreferenceUpdateRequest(dashboards_table_density="wide")

    def test_serialize_roundtrip(self):
        req = ProfilePreferenceUpdateRequest(superset_username="admin", email_address="a@b.com")
        data = req.model_dump()
        restored = ProfilePreferenceUpdateRequest.model_validate(data)
        assert restored.superset_username == "admin"
        assert restored.email_address == "a@b.com"


class TestProfilePreferenceResponse:
    """ProfilePreferenceResponse — response envelope with preference + security."""

    def test_defaults(self):
        ts = datetime.now(timezone.utc)
        pref = ProfilePreference(user_id="u-1", created_at=ts, updated_at=ts)
        resp = ProfilePreferenceResponse(preference=pref)
        assert resp.status == "success"
        assert resp.message is None
        assert resp.validation_errors == []
        assert resp.security.read_only is True

    def test_with_errors(self):
        ts = datetime.now(timezone.utc)
        pref = ProfilePreference(user_id="u-1", created_at=ts, updated_at=ts)
        resp = ProfilePreferenceResponse(
            preference=pref, status="error", message="Failed",
            validation_errors=["username required"],
        )
        assert resp.status == "error"
        assert resp.validation_errors == ["username required"]

    def test_serialize_roundtrip(self):
        ts = datetime.now(timezone.utc)
        pref = ProfilePreference(user_id="u-1", created_at=ts, updated_at=ts)
        resp = ProfilePreferenceResponse(preference=pref)
        data = resp.model_dump()
        restored = ProfilePreferenceResponse.model_validate(data)
        assert restored.status == "success"
        assert restored.preference.user_id == "u-1"


class TestSupersetAccountLookupRequest:
    """SupersetAccountLookupRequest — pagination with constraints."""

    def test_defaults(self):
        req = SupersetAccountLookupRequest(environment_id="env-1")
        assert req.environment_id == "env-1"
        assert req.search is None
        assert req.page_index == 0
        assert req.page_size == 20
        assert req.sort_column == "username"
        assert req.sort_order == "desc"

    def test_page_size_constraints(self):
        req = SupersetAccountLookupRequest(environment_id="env-1", page_size=100)
        assert req.page_size == 100
        with pytest.raises(ValidationError):
            SupersetAccountLookupRequest(environment_id="env-1", page_size=0)
        with pytest.raises(ValidationError):
            SupersetAccountLookupRequest(environment_id="env-1", page_size=101)

    def test_page_index_ge_zero(self):
        with pytest.raises(ValidationError):
            SupersetAccountLookupRequest(environment_id="env-1", page_index=-1)

    def test_custom_values(self):
        req = SupersetAccountLookupRequest(
            environment_id="env-1", search="admin", page_index=1, page_size=50,
            sort_column="email", sort_order="asc",
        )
        assert req.search == "admin"
        assert req.sort_column == "email"
        assert req.sort_order == "asc"

    def test_serialize_roundtrip(self):
        req = SupersetAccountLookupRequest(environment_id="env-1", search="test")
        data = req.model_dump()
        restored = SupersetAccountLookupRequest.model_validate(data)
        assert restored.search == "test"
        assert restored.environment_id == "env-1"


class TestSupersetAccountCandidate:
    """SupersetAccountCandidate — projected Superset user."""

    def test_minimal(self):
        c = SupersetAccountCandidate(environment_id="env-1", username="admin")
        assert c.environment_id == "env-1"
        assert c.username == "admin"
        assert c.display_name is None
        assert c.email is None
        assert c.is_active is None

    def test_with_all_fields(self):
        c = SupersetAccountCandidate(
            environment_id="env-1", username="jdoe",
            display_name="John Doe", email="j@d.com", is_active=True,
        )
        assert c.display_name == "John Doe"
        assert c.is_active is True

    def test_serialize_roundtrip(self):
        c = SupersetAccountCandidate(environment_id="env-1", username="admin")
        data = c.model_dump()
        restored = SupersetAccountCandidate.model_validate(data)
        assert restored.username == "admin"


class TestSupersetAccountLookupResponse:
    """SupersetAccountLookupResponse — paginated response envelope."""

    def test_minimal(self):
        resp = SupersetAccountLookupResponse(status="success", environment_id="env-1", page_index=0, page_size=20, total=0)
        assert resp.items == []
        assert resp.warning is None

    def test_with_items(self):
        items = [SupersetAccountCandidate(environment_id="env-1", username="admin")]
        resp = SupersetAccountLookupResponse(
            status="success", environment_id="env-1",
            page_index=0, page_size=20, total=1, items=items,
        )
        assert len(resp.items) == 1

    def test_degraded_status(self):
        resp = SupersetAccountLookupResponse(
            status="degraded", environment_id="env-1",
            page_index=0, page_size=20, total=0,
            warning="Cannot load accounts",
        )
        assert resp.status == "degraded"
        assert resp.warning == "Cannot load accounts"

    def test_constraints(self):
        with pytest.raises(ValidationError):
            SupersetAccountLookupResponse(status="success", environment_id="env-1", page_index=0, page_size=0, total=0)
        with pytest.raises(ValidationError):
            SupersetAccountLookupResponse(status="success", environment_id="env-1", page_index=0, page_size=101, total=0)
        with pytest.raises(ValidationError):
            SupersetAccountLookupResponse(status="success", environment_id="env-1", page_index=-1, page_size=20, total=0)
        with pytest.raises(ValidationError):
            SupersetAccountLookupResponse(status="success", environment_id="env-1", page_index=0, page_size=20, total=-1)
        with pytest.raises(ValidationError):
            SupersetAccountLookupResponse(status="invalid", environment_id="env-1", page_index=0, page_size=20, total=0)

    def test_serialize_roundtrip(self):
        resp = SupersetAccountLookupResponse(status="success", environment_id="env-1", page_index=0, page_size=20, total=5)
        data = resp.model_dump()
        restored = SupersetAccountLookupResponse.model_validate(data)
        assert restored.total == 5
        assert restored.status == "success"
# #endregion Test.Schemas.Profile
