from datetime import datetime, timedelta, timezone as dt_timezone
from unittest.mock import Mock, patch

from allianceauth.tests.auth_utils import AuthUtils
from allianceauth.utils.testing import NoSocketsTestCase

from drifters.models import Wormhole


class TestWormholeModel(NoSocketsTestCase):
    def test_set_eol_updates_decaying_hole(self) -> None:
        now = datetime(2026, 3, 25, 12, 0, tzinfo=dt_timezone.utc)
        user = AuthUtils.create_user("test-user", disconnect_signals=True)
        wormhole = Wormhole(lifetime=Wormhole.Lifetime.DECAY)
        wormhole.save = Mock()

        with patch("drifters.models.timezone.now", side_effect=[now, now]):
            result = wormhole.set_eol(user=user)

        self.assertTrue(result)
        self.assertEqual(wormhole.lifetime, Wormhole.Lifetime.EOL)
        self.assertEqual(wormhole.updated_by, user)
        self.assertEqual(wormhole.updated_at, now)
        self.assertEqual(wormhole.eol_changed_at, now)
        wormhole.save.assert_called_once_with()

    def test_set_eol_does_nothing_for_non_decaying_hole(self) -> None:
        wormhole = Wormhole(lifetime=Wormhole.Lifetime.FRESH)
        wormhole.save = Mock()

        result = wormhole.set_eol(user=Mock())

        self.assertFalse(result)
        self.assertEqual(wormhole.lifetime, Wormhole.Lifetime.FRESH)
        wormhole.save.assert_not_called()

    def test_set_archived_sets_fields_and_saves(self) -> None:
        now = datetime(2026, 3, 25, 12, 0, tzinfo=dt_timezone.utc)
        user = AuthUtils.create_user("archive-user", disconnect_signals=True)
        wormhole = Wormhole(lifetime=Wormhole.Lifetime.FRESH)
        wormhole.save = Mock()

        with patch("drifters.models.timezone.now", return_value=now):
            result = wormhole.set_archived(user=user)

        self.assertTrue(result)
        self.assertTrue(wormhole.archived)
        self.assertEqual(wormhole.archived_at, now)
        self.assertEqual(wormhole.archived_by, user)
        wormhole.save.assert_called_once_with()

    def test_formatted_lifetime_for_fresh_hole(self) -> None:
        now = datetime(2026, 3, 25, 12, 0, tzinfo=dt_timezone.utc)
        wormhole = Wormhole(
            lifetime=Wormhole.Lifetime.FRESH,
            created_at=now - timedelta(hours=8),
        )

        with patch("drifters.models.timezone.now", return_value=now):
            result = wormhole.formatted_lifetime

        self.assertEqual(result, "Fresh 50%")

    def test_formatted_lifetime_for_eol_without_eol_changed(self) -> None:
        now = datetime(2026, 3, 25, 12, 0, tzinfo=dt_timezone.utc)
        wormhole = Wormhole(
            lifetime=Wormhole.Lifetime.EOL,
            created_at=now - timedelta(hours=1),
            eol_changed_at=None,
        )

        with patch("drifters.models.timezone.now", return_value=now):
            result = wormhole.formatted_lifetime

        self.assertEqual(result, "EOL 25%")

    def test_formatted_lifetime_for_eol_with_eol_changed(self) -> None:
        now = datetime(2026, 3, 25, 12, 0, tzinfo=dt_timezone.utc)
        wormhole = Wormhole(
            lifetime=Wormhole.Lifetime.EOL,
            created_at=now - timedelta(hours=3),
            eol_changed_at=now - timedelta(hours=2),
        )

        with patch("drifters.models.timezone.now", return_value=now):
            result = wormhole.formatted_lifetime

        self.assertEqual(result, "EOL 50%")

    def test_formatted_lifetime_motd_decay_green(self) -> None:
        now = datetime(2026, 3, 25, 12, 0, tzinfo=dt_timezone.utc)
        wormhole = Wormhole(
            lifetime=Wormhole.Lifetime.DECAY,
            created_at=now - timedelta(hours=8),
            updated_at=now - timedelta(hours=2),
        )

        with patch("drifters.models.timezone.now", return_value=now):
            result = wormhole.formatted_lifetime_motd

        self.assertIn('color="#ff00ff00"', result)
        self.assertIn("Decaying 50%", result)

    def test_formatted_lifetime_motd_eol_with_changed_yellow(self) -> None:
        now = datetime(2026, 3, 25, 12, 0, tzinfo=dt_timezone.utc)
        wormhole = Wormhole(
            lifetime=Wormhole.Lifetime.EOL,
            created_at=now - timedelta(hours=3),
            updated_at=now - timedelta(hours=2),
            eol_changed_at=now - timedelta(hours=1),
        )

        with patch("drifters.models.timezone.now", return_value=now):
            result = wormhole.formatted_lifetime_motd

        self.assertIn('color="#ffffff00"', result)
        self.assertIn("EOL 25%", result)

    def test_set_archived_without_user(self) -> None:
        now = datetime(2026, 3, 25, 12, 0, tzinfo=dt_timezone.utc)
        wormhole = Wormhole(lifetime=Wormhole.Lifetime.FRESH)
        wormhole.save = Mock()

        with patch("drifters.models.timezone.now", return_value=now):
            result = wormhole.set_archived()

        self.assertTrue(result)
        self.assertTrue(wormhole.archived)
        self.assertIsNone(wormhole.archived_by)
        self.assertEqual(wormhole.archived_at, now)
        wormhole.save.assert_called_once_with()

    def test_complex_id_mapping(self) -> None:
        expected_ids = {
            Wormhole.Complexes.SENTINEL: 31000001,
            Wormhole.Complexes.BARBICAN: 31000002,
            Wormhole.Complexes.VIDETTE: 31000003,
            Wormhole.Complexes.CONFLUX: 31000004,
            Wormhole.Complexes.THERA: 31000005,
            Wormhole.Complexes.REDOUBT: 31000006,
            Wormhole.Complexes.TURNUR: 0,
        }

        for complex_name, expected in expected_ids.items():
            wormhole = Wormhole(complex=complex_name)
            self.assertEqual(wormhole.complex_id, expected)
