import uuid
from datetime import timedelta, datetime, tzinfo

from dateutil.parser import isoparse
import pytz
from django.contrib.auth.hashers import make_password
from django.test import TestCase

from apps.user.models import User
from apps.competition.models import Competition


class CompetitionEndpointTests(TestCase):
    def setUp(self):
        self.user = User.objects.create(
            email="user@example.com",
            password=make_password("password123"),
            username="t1wk4",
        )

        self.competition = Competition.objects.create(
            title="AI Challenge",
            description="Machine Learning Competition",
            type="edu",
            participation_type="solo",
        )

        resp = self.client.post(
            "/api/v1/sign-in",
            data={"email": self.user.email, "password": "password123"},
            content_type="application/json",
        ).json()
        token = resp["token"]

        self.valid_headers = {"HTTP_AUTHORIZATION": f"Bearer {token}"}

    def get_url(self, competition_id):
        return f"/api/v1/competitions/{competition_id}"

    def test_get_competition_success(self):
        response = self.client.get(
            self.get_url(self.competition.id), **self.valid_headers
        )

        self.assertEqual(response.status_code, 200)
        data = response.json()

        self.assertEqual(data["id"], str(self.competition.id))
        self.assertEqual(data["title"], "AI Challenge")
        self.assertEqual(data["type"], "edu")

        self.assertIsNone(data["image_url"])
        self.assertIsNone(data["start_date"])
        self.assertIsNone(data["end_date"])

    def test_invalid_uuid_format(self):
        response = self.client.get(
            self.get_url("invalid-id"), **self.valid_headers
        )
        self.assertEqual(response.status_code, 400)

    def test_unauthenticated_access(self):
        response = self.client.get(self.get_url(self.competition.id))
        self.assertEqual(response.status_code, 401)
        self.assertEqual(response.json()["detail"], "Unauthorized")

    def test_nonexistent_competition(self):
        new_uuid = uuid.uuid4()
        response = self.client.get(
            self.get_url(new_uuid), **self.valid_headers
        )
        self.assertEqual(response.status_code, 404)
        self.assertEqual(response.json()["detail"], "Not Found")

    def test_invalid_auth_token(self):
        response = self.client.get(
            self.get_url(self.competition.id),
            HTTP_AUTHORIZATION="Bearer invalid_token",
        )
        self.assertEqual(response.status_code, 401)
        self.assertEqual(response.json()["detail"], "Unauthorized")

    def test_malformed_auth_header(self):
        cases = [
            ("InvalidScheme valid_token_123", 401),
            ("Bearer", 401),
            ("", 401),
        ]

        for header, expected_status in cases:
            with self.subTest(header=header):
                response = self.client.get(
                    self.get_url(self.competition.id),
                    HTTP_AUTHORIZATION=header,
                )
                self.assertEqual(response.status_code, expected_status)


class CompetitionsEndpointTests(TestCase):
    def setUp(self):
        self.user = User.objects.create(
            email="user@example.com",
            password=make_password("password123"),
            username="t1wk4",
        )

        resp = self.client.post(
            "/api/v1/sign-in",
            data={"email": self.user.email, "password": "password123"},
            content_type="application/json",
        ).json()
        token = resp["token"]

        now = datetime.now(tz=pytz.utc)
        self.competitions = []
        for i in range(1, 6):
            competition = Competition.objects.create(
                title=f"Competition {i}",
                description=f"Description {i}",
                type=(
                    Competition.CompetitionType.EDU
                    if i % 2 == 0
                    else Competition.CompetitionType.COMPETITIVE
                ),
                participation_type=Competition.CompetitionParticipationType.SOLO,
                start_date=(now + timedelta(days=i)).isoformat(),
                end_date=(now + timedelta(days=i + 7)).isoformat(),
            )
            if i <= 2:
                competition.participants.add(self.user)
            self.competitions.append(competition)

        self.valid_headers = {"HTTP_AUTHORIZATION": f"Bearer {token}"}

    def get_url(self, params=None):
        base_url = "/api/v1/competitions"
        return f"{base_url}?{params}" if params else base_url

    def test_get_participating_competitions(self):
        response = self.client.get(
            self.get_url("is_participating=true"), **self.valid_headers
        )

        self.assertEqual(response.status_code, 200)
        data = response.json()
        self.assertEqual(len(data), 2)
        self.assertEqual(
            {item["id"] for item in data},
            {str(self.competitions[0].id), str(self.competitions[1].id)},
        )

    def test_competition_type_values(self):
        response = self.client.get(
            self.get_url("is_participating=true"), **self.valid_headers
        )

        for i in range(len(response.json())):
            item = response.json()[i]
            if (i + 1) % 2 == 0:
                self.assertEqual(item["type"], "edu")
            else:
                self.assertEqual(item["type"], "competitive")

    def test_participation_type_values(self):
        response = self.client.get(
            self.get_url("is_participating=false"), **self.valid_headers
        )

        types = [item["participation_type"] for item in response.json()]
        self.assertCountEqual(types, ["solo", "solo", "solo"])

    def test_datetime_formatting(self):
        response = self.client.get(
            self.get_url("is_participating=true"), **self.valid_headers
        )

        for item in response.json():
            if item["start_date"]:
                try:
                    isoparse(item["start_date"])
                except ValueError:
                    self.fail("Invalid start_date format")
            if item["end_date"]:
                try:
                    isoparse(item["end_date"])
                except ValueError:
                    self.fail("Invalid end_date format")

    def test_competition_metadata(self):
        response = self.client.get(
            self.get_url("is_participating=true"), **self.valid_headers
        )

        item = response.json()[0]
        self.assertEqual(item["title"], "Competition 1")
        self.assertEqual(item["description"], "Description 1")
        self.assertEqual(item["type"], "competitive")
        self.assertEqual(item["participation_type"], "solo")

    def test_verbose_name_consistency(self):
        response = self.client.get(
            self.get_url("is_participating=true"), **self.valid_headers
        )

        item = response.json()[0]
        self.assertNotIn("название", item)
        self.assertIn("title", item)

    def test_null_dates_handling(self):
        competition = Competition.objects.create(
            title="No Dates Competition",
            description="Test competition",
            type=Competition.CompetitionType.EDU,
            participation_type=Competition.CompetitionParticipationType.SOLO,
        )

        response = self.client.get(
            self.get_url("is_participating=false"), **self.valid_headers
        )

        test_item = next(
            item
            for item in response.json()
            if item["id"] == str(competition.id)
        )
        self.assertIsNone(test_item["start_date"])
        self.assertIsNone(test_item["end_date"])

    def test_participation_status_filtering(self):
        response = self.client.get(
            self.get_url("is_participating=false"), **self.valid_headers
        )

        data = response.json()
        self.assertEqual(len(data), 3)
