"""Integration tests for the paid/licensed code path.

These tests verify the full activation → check flow works correctly,
covering bugs that unit tests missed:
- Empty expires_at causing instant expiration
- set[Path] runtime error on Python <3.9
- status command crashing with no expiry date
- Running checks with a license on multiple directories
"""

import json
import os
import tempfile
from datetime import datetime, timedelta
from pathlib import Path
from unittest.mock import patch, MagicMock

import pytest

from stablestack.licensing import (
    check_license,
    save_license_cache,
    load_license_cache,
    invalidate_license_cache,
    _parse_license_info,
    LicenseInfo,
)
from stablestack.runner import run_checks


@pytest.fixture(autouse=True)
def reset_license_cache():
    invalidate_license_cache()
    yield
    invalidate_license_cache()


@pytest.fixture
def license_dir(tmp_path):
    d = tmp_path / ".stablestack"
    d.mkdir()
    return d


# ---------------------------------------------------------------------------
# Bug 1: Empty expires_at should mean "never expires"
# ---------------------------------------------------------------------------

class TestEmptyExpiresAt:
    """Regression tests for licenses with no expiration date."""

    def test_parse_empty_expires_at_gives_none(self):
        """Empty expires_at should parse as None, not datetime.now()."""
        cache = {
            "customer_name": "Test",
            "customer_email": "",
            "license_type": "individual",
            "expires_at": "",
            "max_projects": 0,
        }
        info = _parse_license_info(cache)
        assert info.expires_at is None

    def test_parse_missing_expires_at_gives_none(self):
        """Missing expires_at key should also parse as None."""
        cache = {
            "customer_name": "Test",
            "customer_email": "",
            "license_type": "individual",
            "max_projects": 0,
        }
        info = _parse_license_info(cache)
        assert info.expires_at is None

    def test_license_with_no_expiry_is_valid(self, license_dir):
        """A license with no expires_at should not be considered expired."""
        cache = {
            "key": "sk_live_test_no_expiry",
            "customer_name": "Test User",
            "customer_email": "",
            "license_type": "individual",
            "expires_at": "",
            "max_projects": 0,
            "validated_at": datetime.now().isoformat(),
        }
        with patch("stablestack.licensing.load_license_cache", return_value=cache):
            valid, info, error = check_license()
            assert valid is True, f"Expected valid license but got error: {error}"
            assert info is not None
            assert info.customer_name == "Test User"
            assert error is None

    def test_license_with_future_expiry_is_valid(self, license_dir):
        """Sanity check: license with future date is valid."""
        cache = {
            "key": "sk_live_test_future",
            "customer_name": "Test User",
            "customer_email": "",
            "license_type": "individual",
            "expires_at": (datetime.now() + timedelta(days=365)).isoformat(),
            "max_projects": 0,
            "validated_at": datetime.now().isoformat(),
        }
        with patch("stablestack.licensing.load_license_cache", return_value=cache):
            valid, info, error = check_license()
            assert valid is True
            assert info.expires_at is not None

    def test_license_with_past_expiry_is_invalid(self, license_dir):
        """License with past date should be expired."""
        cache = {
            "key": "sk_live_test_past",
            "customer_name": "Test User",
            "customer_email": "",
            "license_type": "individual",
            "expires_at": (datetime.now() - timedelta(days=1)).isoformat(),
            "max_projects": 0,
            "validated_at": datetime.now().isoformat(),
        }
        with patch("stablestack.licensing.load_license_cache", return_value=cache):
            valid, info, error = check_license()
            assert valid is False
            assert "expired" in error.lower()

    def test_server_returns_empty_expires_at(self, license_dir):
        """Simulates what the real API returns for a no-expiry license."""
        server_response = {
            "valid": True,
            "customer_name": "Eric Lui",
            "customer_email": "",
            "license_type": "individual",
            "expires_at": "",
            "max_projects": 0,
            "download_url": "",
        }
        with patch(
            "stablestack.licensing._get_license_cache_path",
            return_value=license_dir / "license.json",
        ):
            save_license_cache("sk_live_test", server_response)
            cache = load_license_cache()
            assert cache is not None
            assert cache["expires_at"] == ""

            # Now check_license should treat this as valid
            with patch("stablestack.licensing.load_license_cache", return_value=cache):
                valid, info, error = check_license()
                assert valid is True, f"Expected valid but got: {error}"


# ---------------------------------------------------------------------------
# Bug 2: Running checks on multiple directories
# ---------------------------------------------------------------------------

class TestMultiDirectoryCheck:
    """Regression test for set[Path] runtime error on Python <3.9."""

    def test_check_multiple_directories(self, tmp_path):
        """Running checks on multiple directories should not crash."""
        # Create two directories with Python files
        dir_a = tmp_path / "project_a"
        dir_a.mkdir()
        (dir_a / "app.py").write_text("x = 1\n")

        dir_b = tmp_path / "project_b"
        dir_b.mkdir()
        (dir_b / "main.py").write_text("y = 2\n")

        # Run checks on both directories
        findings, is_free, total = run_checks([dir_a, dir_b])
        # Should not raise AttributeError
        assert isinstance(findings, list)

    def test_check_single_directory(self, tmp_path):
        """Single directory should also work (baseline)."""
        d = tmp_path / "project"
        d.mkdir()
        (d / "hello.py").write_text("print('hello')\n")

        findings, is_free, total = run_checks([d])
        assert isinstance(findings, list)

    def test_check_mixed_files_and_dirs(self, tmp_path):
        """Mix of file paths and directory paths should work."""
        d = tmp_path / "project"
        d.mkdir()
        f = d / "app.py"
        f.write_text("x = 1\n")

        d2 = tmp_path / "other"
        d2.mkdir()
        (d2 / "main.py").write_text("y = 2\n")

        findings, is_free, total = run_checks([f, d2])
        assert isinstance(findings, list)


# ---------------------------------------------------------------------------
# Full activation → check flow
# ---------------------------------------------------------------------------

class TestActivationFlow:
    """End-to-end test for activation + running checks with a license."""

    def test_activate_then_check(self, tmp_path, license_dir):
        """Simulate full: activate key → save cache → run checks."""
        server_response = {
            "valid": True,
            "customer_name": "Test User",
            "customer_email": "test@example.com",
            "license_type": "individual",
            "expires_at": "",
            "max_projects": 0,
            "download_url": "",
        }

        # Step 1: Save license (simulates activation)
        with patch(
            "stablestack.licensing._get_license_cache_path",
            return_value=license_dir / "license.json",
        ):
            save_license_cache("sk_live_test_key", server_response)

        # Step 2: Verify license is valid
        cache = json.loads((license_dir / "license.json").read_text())
        with patch("stablestack.licensing.load_license_cache", return_value=cache):
            valid, info, error = check_license()
            assert valid is True, f"License should be valid but got: {error}"
            assert info.customer_name == "Test User"
            assert info.expires_at is None  # No expiry

        # Step 3: Run checks (should use all checkers when licensed)
        project = tmp_path / "project"
        project.mkdir()
        (project / "app.py").write_text(
            "import random\ntoken = random.randint(0, 999999)\n"
        )

        with patch("stablestack.licensing.load_license_cache", return_value=cache), \
             patch("stablestack.runner.get_cached_license", return_value=(True, info, None)):
            findings, is_free, total = run_checks([project])
            assert is_free is False  # Should be licensed
            assert isinstance(findings, list)


# ---------------------------------------------------------------------------
# Status command with no expiry
# ---------------------------------------------------------------------------

class TestStatusWithNoExpiry:
    """Regression test for status command crashing when expires_at is None."""

    def test_license_info_with_none_expiry(self):
        """LicenseInfo with None expires_at should not crash string formatting."""
        info = LicenseInfo(
            customer_name="Test",
            customer_email="",
            license_type="individual",
            expires_at=None,
            max_projects=0,
        )
        # These should not raise
        assert info.expires_at is None
        assert info.customer_name == "Test"

        # The status command does this check — verify it doesn't crash
        if info.expires_at is not None:
            days_left = (info.expires_at - datetime.now()).days
        else:
            days_left = None
        assert days_left is None
