"""
Tests for buybackprogram views.

These tests use raise_request_exception=False and check access control
(redirect vs. no redirect) without depending on full template rendering,
since the AA base template chain requires static assets not present in
the test environment.
"""

from django.test import Client, TestCase
from django.urls import reverse

from .testdata.factories import (
    OwnerFactory,
    ProgramFactory,
    PublicProgramFactory,
    UserIssuerFactory,
    UserProjectManagerFactory,
)
from .testdata.load_eveuniverse import load_eveuniverse

LOGIN_URL = "/account/login/"


def is_login_redirect(response):
    """Returns True if the response is a redirect to the login page."""
    return response.status_code in (301, 302) and LOGIN_URL in response.get(
        "Location", ""
    )


def was_not_redirected_to_login(response):
    """Returns True if the view was reached (not blocked by AA auth middleware)."""
    return not is_login_redirect(response)


class TestIndexViewPublic(TestCase):
    """Index page is accessible without login and shows only public programs."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.client = Client(raise_request_exception=False)

    def test_anonymous_user_can_access_index(self):
        response = self.client.get(reverse("buybackprogram:index"))
        self.assertTrue(was_not_redirected_to_login(response))

    def test_anonymous_user_sees_only_public_programs(self):
        public = PublicProgramFactory()
        private = ProgramFactory(is_public=False)
        response = self.client.get(reverse("buybackprogram:index"))
        self.assertTrue(was_not_redirected_to_login(response))
        programs = list(response.context["programs"])
        self.assertIn(public, programs)
        self.assertNotIn(private, programs)

    def test_anonymous_user_sees_no_programs_when_none_are_public(self):
        ProgramFactory(is_public=False)
        response = self.client.get(reverse("buybackprogram:index"))
        self.assertTrue(was_not_redirected_to_login(response))
        self.assertEqual(len(list(response.context["programs"])), 0)


class TestIndexViewAuthenticated(TestCase):
    """Authenticated users see programs filtered by their groups/state."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.user = UserIssuerFactory()
        self.client = Client(raise_request_exception=False)
        self.client.force_login(self.user)

    def test_authenticated_user_can_access_index(self):
        response = self.client.get(reverse("buybackprogram:index"))
        self.assertTrue(was_not_redirected_to_login(response))

    def test_authenticated_user_sees_unrestricted_programs(self):
        program = ProgramFactory()  # no group/state restrictions
        response = self.client.get(reverse("buybackprogram:index"))
        self.assertIn(program, list(response.context["programs"]))


class TestCalculateViewPublicProgram(TestCase):
    """Anonymous users can access the calculate page for public programs."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.client = Client(raise_request_exception=False)

    def test_anonymous_can_access_calculate_for_public_program(self):
        program = PublicProgramFactory()
        response = self.client.get(
            reverse(
                "buybackprogram:program_calculate", kwargs={"program_pk": program.pk}
            )
        )
        self.assertTrue(was_not_redirected_to_login(response))

    def test_anonymous_redirected_for_private_program(self):
        program = ProgramFactory(is_public=False)
        response = self.client.get(
            reverse(
                "buybackprogram:program_calculate", kwargs={"program_pk": program.pk}
            )
        )
        # Should redirect to buybackprogram:index, not login
        self.assertIn(response.status_code, (301, 302))
        self.assertNotIn(LOGIN_URL, response.get("Location", ""))

    def test_anonymous_redirected_for_nonexistent_program(self):
        response = self.client.get(
            reverse("buybackprogram:program_calculate", kwargs={"program_pk": 99999})
        )
        # View returns redirect for missing program; template errors show as 500
        # — both indicate the view was reached and not blocked by login middleware
        self.assertNotIn(LOGIN_URL, response.get("Location", ""))


class TestCalculateViewAuthenticated(TestCase):
    """Authenticated users can access the calculate page for any visible program."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.user = UserIssuerFactory()
        self.client = Client(raise_request_exception=False)
        self.client.force_login(self.user)

    def test_authenticated_can_access_calculate_for_private_program(self):
        program = ProgramFactory(is_public=False)
        response = self.client.get(
            reverse(
                "buybackprogram:program_calculate", kwargs={"program_pk": program.pk}
            )
        )
        self.assertTrue(was_not_redirected_to_login(response))

    def test_authenticated_redirected_for_nonexistent_program(self):
        response = self.client.get(
            reverse("buybackprogram:program_calculate", kwargs={"program_pk": 99999})
        )
        self.assertNotIn(LOGIN_URL, response.get("Location", ""))


class TestReverseIndexView(TestCase):
    """Reverse buyback index is accessible publicly and shows public reverse programs."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.client = Client(raise_request_exception=False)

    def test_anonymous_can_access_reverse_index(self):
        response = self.client.get(reverse("buybackprogram:reverse_index"))
        self.assertTrue(was_not_redirected_to_login(response))

    def test_anonymous_sees_only_public_reverse_programs(self):
        from .testdata.factories import (
            PublicReverseProgramFactory,
            ReverseProgramFactory,
        )

        public = PublicReverseProgramFactory()
        private = ReverseProgramFactory(is_public=False)
        response = self.client.get(reverse("buybackprogram:reverse_index"))
        self.assertTrue(was_not_redirected_to_login(response))
        programs = list(response.context["programs"])
        self.assertIn(public, programs)
        self.assertNotIn(private, programs)

    def test_manager_sees_own_private_reverse_programs(self):
        from .testdata.factories import ReverseProgramFactory

        manager = UserProjectManagerFactory()
        self.client.force_login(manager)
        owner = OwnerFactory(user=manager)
        program = ReverseProgramFactory(owner=owner, is_public=False)
        response = self.client.get(reverse("buybackprogram:reverse_index"))
        programs = list(response.context["programs"])
        self.assertIn(program, programs)


class TestReverseCalculateViewPublic(TestCase):
    """Anonymous users can access reverse calculate for public reverse programs."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.client = Client(raise_request_exception=False)

    def test_anonymous_can_access_reverse_calculate_for_public_program(self):
        from .testdata.factories import PublicReverseProgramFactory

        program = PublicReverseProgramFactory()
        response = self.client.get(
            reverse(
                "buybackprogram:reverse_program_calculate",
                kwargs={"program_pk": program.pk},
            )
        )
        self.assertTrue(was_not_redirected_to_login(response))

    def test_anonymous_redirected_for_private_reverse_program(self):
        from .testdata.factories import ReverseProgramFactory

        program = ReverseProgramFactory(is_public=False)
        response = self.client.get(
            reverse(
                "buybackprogram:reverse_program_calculate",
                kwargs={"program_pk": program.pk},
            )
        )
        self.assertIn(response.status_code, (301, 302))
        self.assertNotIn(LOGIN_URL, response.get("Location", ""))

    def test_anonymous_redirected_for_nonexistent_reverse_program(self):
        response = self.client.get(
            reverse(
                "buybackprogram:reverse_program_calculate",
                kwargs={"program_pk": 99999},
            )
        )
        self.assertIn(response.status_code, (301, 302))
        self.assertNotIn(LOGIN_URL, response.get("Location", ""))


class TestReverseCalculateStockDisplay(TestCase):
    """Stock items from HangarStock appear in the calculate page context."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.client = Client(raise_request_exception=False)

    def test_stock_data_in_context_for_public_program(self):
        from .testdata.factories import (
            HangarStockFactory,
            ItemPricesFactory,
            PublicReverseProgramFactory,
        )

        program = PublicReverseProgramFactory()
        stock = HangarStockFactory(program=program)
        ItemPricesFactory(eve_type=stock.eve_type)
        response = self.client.get(
            reverse(
                "buybackprogram:reverse_program_calculate",
                kwargs={"program_pk": program.pk},
            )
        )
        self.assertTrue(was_not_redirected_to_login(response))
        stock_data = response.context.get("stock_data", [])
        type_ids = [item["type_id"] for item in stock_data]
        self.assertIn(stock.eve_type_id, type_ids)


class TestManagementViewsRequireLogin(TestCase):
    """Management views are not accessible without authentication."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.client = Client(raise_request_exception=False)

    def test_program_add_requires_login(self):
        response = self.client.get(reverse("buybackprogram:program_add"))
        self.assertTrue(is_login_redirect(response))

    def test_reverse_program_add_requires_login(self):
        response = self.client.get(reverse("buybackprogram:reverse_program_add"))
        self.assertTrue(is_login_redirect(response))

    def test_manual_hangar_sync_requires_login(self):
        response = self.client.get(reverse("buybackprogram:manual_hangar_sync"))
        self.assertTrue(is_login_redirect(response))


class TestMyReverseStatsView(TestCase):
    """My reverse stats page is accessible to authenticated users."""

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        load_eveuniverse()

    def setUp(self):
        self.user = UserIssuerFactory()
        self.client = Client(raise_request_exception=False)
        self.client.force_login(self.user)

    def test_authenticated_user_can_access_reverse_stats(self):
        response = self.client.get(reverse("buybackprogram:reverse_my_stats"))
        self.assertTrue(was_not_redirected_to_login(response))

    def test_unauthenticated_user_cannot_access_reverse_stats(self):
        anon_client = Client(raise_request_exception=False)
        response = anon_client.get(reverse("buybackprogram:reverse_my_stats"))
        self.assertTrue(is_login_redirect(response))

    def test_reverse_stats_shows_no_contracts_when_none_exist(self):
        response = self.client.get(reverse("buybackprogram:reverse_my_stats"))
        contracts = list(response.context.get("contracts", []))
        self.assertEqual(len(contracts), 0)
