"""Tests for all result classes."""

from __future__ import annotations

import time
from datetime import datetime, timezone

from licensesos.results.activation import ActivationResult
from licensesos.results.activations import ActivationsResult
from licensesos.results.deactivation import DeactivationResult
from licensesos.results.heartbeat import HeartbeatResult
from licensesos.results.offline_validation import OfflineValidationResult
from licensesos.results.usage import UsageResult
from licensesos.results.validation import ValidationResult


# ---------------------------------------------------------------------------
# ValidationResult
# ---------------------------------------------------------------------------


class TestValidationResult:
    valid_data: dict = {
        "valid": True,
        "license": {
            "id": "lic_123",
            "status": "active",
            "entitlements": {"pro": True, "seats": 5},
            "expires_at": "2025-12-31T00:00:00+00:00",
            "is_expired": False,
            "activation_limit": 3,
            "activation_count": 1,
            "uses_count": 10,
            "max_uses": 100,
            "uses_remaining": 90,
            "usage_reset_interval": "monthly",
            "uses_reset_at": "2025-02-01T00:00:00+00:00",
        },
        "activated": True,
        "activation": {
            "id": "act_456",
            "identifier": "example.com",
            "last_seen_at": "2025-01-15T12:00:00+00:00",
        },
        "offline_token": "some.token",
    }

    invalid_data: dict = {
        "valid": False,
        "error": {"code": "LICENSE_NOT_FOUND", "message": "License not found"},
    }

    def test_parses_valid_response(self) -> None:
        result = ValidationResult(
            valid=self.valid_data["valid"],
            license=self.valid_data["license"],
            activated=self.valid_data["activated"],
            activation=self.valid_data["activation"],
            offline_token=self.valid_data["offline_token"],
        )
        assert result.is_valid() is True
        assert result.is_active() is True
        assert result.is_expired() is False
        assert result.is_revoked() is False
        assert result.activated is True
        assert result.offline_token == "some.token"

    def test_parses_invalid_response(self) -> None:
        result = ValidationResult(
            valid=self.invalid_data["valid"],
            error=self.invalid_data["error"],
        )
        assert result.is_valid() is False
        assert result.error is not None
        assert result.error["code"] == "LICENSE_NOT_FOUND"

    def test_entitlement_lookups(self) -> None:
        result = ValidationResult(
            valid=self.valid_data["valid"],
            license=self.valid_data["license"],
        )
        assert result.has_entitlement("pro") is True
        assert result.has_entitlement("missing") is False
        assert result.get_entitlement("seats") == 5
        assert result.get_entitlement("missing", "default") == "default"

    def test_returns_none_entitlements_for_invalid_result(self) -> None:
        result = ValidationResult(valid=False)
        assert result.has_entitlement("pro") is False
        assert result.get_entitlement("pro", "default") == "default"

    def test_calculates_remaining_activations(self) -> None:
        result = ValidationResult(
            valid=True,
            license=self.valid_data["license"],
        )
        assert result.get_remaining_activations() == 2  # limit 3 - count 1

    def test_returns_none_remaining_activations_when_no_limit(self) -> None:
        license_data = {**self.valid_data["license"], "activation_limit": None}
        result = ValidationResult(valid=True, license=license_data)
        assert result.get_remaining_activations() is None

    def test_is_expired_reads_is_expired_field(self) -> None:
        license_data = {**self.valid_data["license"], "is_expired": True, "status": "active"}
        result = ValidationResult(valid=True, license=license_data)
        assert result.is_expired() is True

    def test_is_expired_falls_back_to_status(self) -> None:
        result = ValidationResult(
            valid=False,
            license={"status": "expired"},
            error={"code": "LICENSE_EXPIRED", "message": "License has expired"},
        )
        assert result.is_expired() is True

    def test_to_dict(self) -> None:
        result = ValidationResult(
            valid=self.valid_data["valid"],
            license=self.valid_data["license"],
            activated=self.valid_data["activated"],
            activation=self.valid_data["activation"],
            offline_token=self.valid_data["offline_token"],
        )
        d = result.to_dict()
        assert d["valid"] is True
        assert d["offline_token"] == "some.token"
        assert d["activation"]["identifier"] == "example.com"


# ---------------------------------------------------------------------------
# ActivationResult
# ---------------------------------------------------------------------------


class TestActivationResult:
    data = {
        "activation": {
            "id": "act_123",
            "identifier": "device-abc",
            "label": "My Laptop",
            "status": "active",
            "activated_at": "2025-01-15T12:00:00+00:00",
            "last_seen_at": None,
            "metadata": {"os": "linux"},
        },
        "license": {
            "id": "lic_123",
            "status": "active",
            "entitlements": None,
            "expires_at": None,
            "activation_count": 2,
            "activation_limit": 5,
        },
    }

    def test_reports_activated_status(self) -> None:
        result = ActivationResult(**self.data)
        assert result.is_activated() is True

    def test_calculates_remaining_activations(self) -> None:
        result = ActivationResult(**self.data)
        assert result.get_remaining_activations() == 3  # 5 - 2

    def test_returns_none_when_no_activation_limit(self) -> None:
        data = {
            **self.data,
            "license": {**self.data["license"], "activation_limit": None},
        }
        result = ActivationResult(**data)
        assert result.get_remaining_activations() is None

    def test_to_dict(self) -> None:
        result = ActivationResult(**self.data)
        d = result.to_dict()
        assert d["activation"]["identifier"] == "device-abc"
        assert d["license"]["activation_count"] == 2


# ---------------------------------------------------------------------------
# DeactivationResult
# ---------------------------------------------------------------------------


class TestDeactivationResult:
    def test_reports_deactivation_success(self) -> None:
        result = DeactivationResult(deactivated=True)
        assert result.is_deactivated() is True

    def test_reports_deactivation_failure(self) -> None:
        result = DeactivationResult(deactivated=False)
        assert result.is_deactivated() is False

    def test_to_dict(self) -> None:
        result = DeactivationResult(deactivated=True)
        assert result.to_dict() == {"deactivated": True}


# ---------------------------------------------------------------------------
# ActivationsResult
# ---------------------------------------------------------------------------


class TestActivationsResult:
    data = {
        "activations": [
            {
                "id": "act_1",
                "identifier": "device-a",
                "label": None,
                "status": "active",
                "activated_at": "2025-01-01T00:00:00+00:00",
                "last_seen_at": None,
                "metadata": None,
            },
            {
                "id": "act_2",
                "identifier": "device-b",
                "label": "Test",
                "status": "active",
                "activated_at": "2025-01-02T00:00:00+00:00",
                "last_seen_at": None,
                "metadata": None,
            },
        ],
        "count": 2,
        "limit": 5,
    }

    def test_returns_active_activations(self) -> None:
        result = ActivationsResult(**self.data)
        assert len(result.get_active_activations()) == 2
        assert result.get_active_count() == 2

    def test_checks_for_activation_by_identifier(self) -> None:
        result = ActivationsResult(**self.data)
        assert result.has_activation("device-a") is True
        assert result.has_activation("device-c") is False

    def test_to_dict(self) -> None:
        result = ActivationsResult(**self.data)
        d = result.to_dict()
        assert d["count"] == 2
        assert d["limit"] == 5


# ---------------------------------------------------------------------------
# HeartbeatResult
# ---------------------------------------------------------------------------


class TestHeartbeatResult:
    def test_returns_next_check_in_date(self) -> None:
        future = datetime.now(timezone.utc).replace(microsecond=0)
        future_str = future.isoformat()
        result = HeartbeatResult(
            activation={
                "id": "act_123",
                "identifier": "device-abc",
                "label": None,
                "status": "active",
                "activated_at": "2025-01-15T12:00:00+00:00",
                "last_seen_at": None,
                "metadata": None,
            },
            next_check_in_at=future_str,
        )
        dt = result.get_next_check_in()
        assert isinstance(dt, datetime)

    def test_calculates_seconds_until_next_check_in(self) -> None:
        # 1 hour from now
        future = datetime.now(timezone.utc).replace(microsecond=0)
        from datetime import timedelta

        future = future + timedelta(hours=1)
        future_str = future.isoformat()
        result = HeartbeatResult(
            activation={
                "id": "act_123",
                "identifier": "device-abc",
                "label": None,
                "status": "active",
                "activated_at": "2025-01-15T12:00:00+00:00",
                "last_seen_at": None,
                "metadata": None,
            },
            next_check_in_at=future_str,
        )
        seconds = result.get_seconds_until_next_check_in()
        # Should be roughly 3600 seconds, give or take a bit
        assert seconds > 3500
        assert seconds <= 3600

    def test_returns_zero_for_past_check_in(self) -> None:
        result = HeartbeatResult(
            activation={
                "id": "act_123",
                "identifier": "device-abc",
                "label": None,
                "status": "active",
                "activated_at": "2025-01-15T12:00:00+00:00",
                "last_seen_at": None,
                "metadata": None,
            },
            next_check_in_at="2020-01-01T00:00:00+00:00",
        )
        assert result.get_seconds_until_next_check_in() == 0.0

    def test_to_dict(self) -> None:
        result = HeartbeatResult(
            activation={"id": "act_1"},
            next_check_in_at="2025-06-01T00:00:00+00:00",
        )
        d = result.to_dict()
        assert d["next_check_in_at"] == "2025-06-01T00:00:00+00:00"


# ---------------------------------------------------------------------------
# UsageResult
# ---------------------------------------------------------------------------


class TestUsageResult:
    def test_detects_limit_reached(self) -> None:
        result = UsageResult(
            usage={
                "uses_count": 100,
                "max_uses": 100,
                "uses_remaining": 0,
                "usage_reset_interval": None,
                "uses_reset_at": None,
            }
        )
        assert result.is_limit_reached() is True

    def test_returns_false_for_limit_reached_when_no_max(self) -> None:
        result = UsageResult(
            usage={
                "uses_count": 999,
                "max_uses": None,
                "uses_remaining": None,
                "usage_reset_interval": None,
                "uses_reset_at": None,
            }
        )
        assert result.is_limit_reached() is False

    def test_calculates_usage_percentage(self) -> None:
        result = UsageResult(
            usage={
                "uses_count": 75,
                "max_uses": 100,
                "uses_remaining": 25,
                "usage_reset_interval": "monthly",
                "uses_reset_at": "2025-02-01T00:00:00+00:00",
            }
        )
        assert result.get_usage_percentage() == 75.0

    def test_returns_none_percentage_when_no_max(self) -> None:
        result = UsageResult(
            usage={
                "uses_count": 50,
                "max_uses": None,
                "uses_remaining": None,
                "usage_reset_interval": None,
                "uses_reset_at": None,
            }
        )
        assert result.get_usage_percentage() is None

    def test_caps_percentage_at_100(self) -> None:
        result = UsageResult(
            usage={
                "uses_count": 150,
                "max_uses": 100,
                "uses_remaining": 0,
                "usage_reset_interval": None,
                "uses_reset_at": None,
            }
        )
        assert result.get_usage_percentage() == 100.0

    def test_returns_reset_date(self) -> None:
        result = UsageResult(
            usage={
                "uses_count": 10,
                "max_uses": 100,
                "uses_remaining": 90,
                "usage_reset_interval": "monthly",
                "uses_reset_at": "2025-02-01T00:00:00+00:00",
            }
        )
        dt = result.get_reset_date()
        assert isinstance(dt, datetime)

    def test_returns_none_reset_date_when_not_configured(self) -> None:
        result = UsageResult(
            usage={
                "uses_count": 10,
                "max_uses": None,
                "uses_remaining": None,
                "usage_reset_interval": None,
                "uses_reset_at": None,
            }
        )
        assert result.get_reset_date() is None

    def test_to_dict(self) -> None:
        usage_data = {
            "uses_count": 10,
            "max_uses": 100,
            "uses_remaining": 90,
            "usage_reset_interval": "monthly",
            "uses_reset_at": "2025-02-01T00:00:00+00:00",
        }
        result = UsageResult(usage=usage_data)
        assert result.to_dict() == {"usage": usage_data}


# ---------------------------------------------------------------------------
# OfflineValidationResult
# ---------------------------------------------------------------------------


class TestOfflineValidationResult:
    def test_creates_valid_result_from_payload(self) -> None:
        payload = {
            "license_id": "lic_123",
            "status": "active",
            "entitlements": {"pro": True},
            "expires_at": "2025-12-31T00:00:00+00:00",
            "activation_limit": 3,
            "activation_count": 1,
            "identifier": "example.com",
            "activated": True,
            "uses_count": 10,
            "max_uses": 100,
            "iat": int(time.time()) - 60,
            "exp": int(time.time()) + 3600,
            "ttl": 3660,
        }
        result = OfflineValidationResult.from_payload(payload, valid=True)
        assert result.is_valid() is True
        assert result.is_expired() is False
        assert result.remaining_seconds() > 3500
        assert result.license_id == "lic_123"
        assert result.status == "active"
        assert result.entitlements == {"pro": True}

    def test_creates_invalid_result(self) -> None:
        result = OfflineValidationResult.invalid("INVALID_SIGNATURE")
        assert result.is_valid() is False
        assert result.error_code == "INVALID_SIGNATURE"
        assert result.license_id is None

    def test_creates_expired_result(self) -> None:
        payload = {
            "license_id": "lic_123",
            "status": "active",
            "entitlements": {"pro": True},
            "expires_at": "2025-12-31T00:00:00+00:00",
            "activation_limit": 3,
            "activation_count": 1,
            "identifier": "example.com",
            "activated": True,
            "uses_count": 10,
            "max_uses": 100,
            "iat": int(time.time()) - 60,
            "exp": int(time.time()) - 60,
            "ttl": 3660,
        }
        result = OfflineValidationResult.expired(payload)
        assert result.is_valid() is False
        assert result.error_code == "TOKEN_EXPIRED"
        assert result.is_expired() is True
        assert result.remaining_seconds() == 0

    def test_to_dict(self) -> None:
        payload = {
            "license_id": "lic_123",
            "status": "active",
            "entitlements": {"pro": True},
            "expires_at": "2025-12-31T00:00:00+00:00",
            "activation_limit": 3,
            "activation_count": 1,
            "identifier": "example.com",
            "activated": True,
            "uses_count": 10,
            "max_uses": 100,
            "iat": int(time.time()) - 60,
            "exp": int(time.time()) + 3600,
            "ttl": 3660,
        }
        result = OfflineValidationResult.from_payload(payload, valid=True)
        d = result.to_dict()
        assert d["valid"] is True
        assert d["license_id"] == "lic_123"
        assert d["expires_at_timestamp"] == payload["exp"]
        assert d["issued_at"] == payload["iat"]
