"""Tests for aicert_pro.license module."""

import base64
import json
import os
import pytest
from datetime import datetime, timedelta, timezone
from pathlib import Path
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
from unittest.mock import patch

from aicert_pro.license import (
    LicenseData,
    LicenseError,
    LicenseMissingError,
    LicenseInvalidError,
    LicenseExpiredError,
    verify_license,
    read_license_from_env,
    require_pro,
    LICENSE_ENV_VAR,
)


@pytest.fixture
def ed25519_key_pair():
    """Generate Ed25519 key pair for testing."""
    private_key = ed25519.Ed25519PrivateKey.generate()
    public_key = private_key.public_key()
    return private_key, public_key


@pytest.fixture
def sample_license_data():
    """Create sample license data for testing."""
    return LicenseData(
        license_id="test-license-123",
        owner="test@example.com",
        issued_at=datetime.now(timezone.utc).isoformat(),
        expires_at=None,
        features=["baseline", "advanced"],
        signature=None,
    )


@pytest.fixture
def valid_license(ed25519_key_pair, sample_license_data):
    """Create a valid signed license."""
    private_key, _ = ed25519_key_pair
    
    # Create signable payload
    payload = f"{sample_license_data.license_id}:{sample_license_data.owner}:{sample_license_data.issued_at}"
    if sample_license_data.expires_at:
        payload += f":{sample_license_data.expires_at}"
    if sample_license_data.features:
        payload += f":{','.join(sample_license_data.features)}"
    
    # Sign the payload
    signature = private_key.sign(payload.encode("utf-8"))
    
    # Create license with signature
    license_data = LicenseData(
        license_id=sample_license_data.license_id,
        owner=sample_license_data.owner,
        issued_at=sample_license_data.issued_at,
        expires_at=sample_license_data.expires_at,
        features=sample_license_data.features,
        signature=base64.b64encode(signature).decode("utf-8"),
    )
    
    return license_data


def test_license_data_is_expired_no_expiry(sample_license_data):
    """Test that license with no expiry is not expired."""
    assert sample_license_data.is_expired is False


def test_license_data_is_expired_future_expiry(sample_license_data):
    """Test that license with future expiry is not expired."""
    sample_license_data.expires_at = (datetime.now(timezone.utc) + timedelta(days=30)).isoformat()
    assert sample_license_data.is_expired is False


def test_license_data_is_expired_past_expiry(sample_license_data):
    """Test that license with past expiry is expired."""
    sample_license_data.expires_at = (datetime.now(timezone.utc) - timedelta(days=1)).isoformat()
    assert sample_license_data.is_expired is True


def test_verify_valid_license(ed25519_key_pair, valid_license):
    """Test verification of a valid license."""
    _, public_key = ed25519_key_pair
    with patch("aicert_pro.license.LICENSE_PUBLIC_KEY", public_key):
        result = verify_license(valid_license)
    assert result is True


def test_verify_expired_license(ed25519_key_pair, sample_license_data):
    """Test that expired license raises error."""
    sample_license_data.expires_at = (datetime.now(timezone.utc) - timedelta(days=1)).isoformat()
    sample_license_data.signature = "dGVzdA=="  # Fake signature
    _, public_key = ed25519_key_pair
    with patch("aicert_pro.license.LICENSE_PUBLIC_KEY", public_key):
        with pytest.raises(LicenseExpiredError):
            verify_license(sample_license_data)


def test_verify_missing_signature(sample_license_data):
    """Test that license without signature raises error."""
    sample_license_data.signature = None
    with pytest.raises(LicenseInvalidError):
        verify_license(sample_license_data)


def test_verify_invalid_signature(ed25519_key_pair, sample_license_data):
    """Test that license with invalid signature raises error."""
    sample_license_data.signature = "dGVzdA=="  # Fake signature
    _, public_key = ed25519_key_pair
    with patch("aicert_pro.license.LICENSE_PUBLIC_KEY", public_key):
        with pytest.raises(LicenseInvalidError):
            verify_license(sample_license_data)


def test_read_license_from_env_missing(monkeypatch):
    """Test that missing environment variable raises error."""
    monkeypatch.delenv(LICENSE_ENV_VAR, raising=False)
    with pytest.raises(LicenseMissingError):
        read_license_from_env()


def test_read_license_from_env_exists(monkeypatch, valid_license):
    """Test reading license from environment variable."""
    license_json = json.dumps({
        "license_id": valid_license.license_id,
        "owner": valid_license.owner,
        "issued_at": valid_license.issued_at,
        "expires_at": valid_license.expires_at,
        "features": valid_license.features,
        "signature": valid_license.signature,
    })
    license_str = base64.b64encode(license_json.encode("utf-8")).decode("utf-8")
    monkeypatch.setenv(LICENSE_ENV_VAR, license_str)
    
    result = read_license_from_env()
    assert result == license_str


def test_require_pro_valid(monkeypatch, ed25519_key_pair, valid_license):
    """Test require_pro with valid license."""
    _, public_key = ed25519_key_pair
    license_json = json.dumps({
        "license_id": valid_license.license_id,
        "owner": valid_license.owner,
        "issued_at": valid_license.issued_at,
        "expires_at": valid_license.expires_at,
        "features": valid_license.features,
        "signature": valid_license.signature,
    })
    license_str = base64.b64encode(license_json.encode("utf-8")).decode("utf-8")
    monkeypatch.setenv(LICENSE_ENV_VAR, license_str)
    
    with patch("aicert_pro.license.LICENSE_PUBLIC_KEY", public_key):
        result = require_pro()
    assert result.license_id == valid_license.license_id


def test_require_pro_missing(monkeypatch):
    """Test require_pro with missing license."""
    monkeypatch.delenv(LICENSE_ENV_VAR, raising=False)
    with pytest.raises(LicenseMissingError):
        require_pro()


def test_require_pro_invalid(monkeypatch):
    """Test require_pro with invalid license."""
    monkeypatch.setenv(LICENSE_ENV_VAR, "invalid-base64-data")
    with pytest.raises(LicenseInvalidError):
        require_pro()
