"""
Tests for Scenario Creation Endpoint Error Handling.

Tests comprehensive error handling for POST /api/v1/package/{uuid}/scenario/
including package not found, permission denied, validation errors, and database errors.
"""

from django.test import TestCase, tag
import json
from uuid import uuid4

from epdb.logic import PackageManager, UserManager
from epdb.models import Scenario


@tag("api", "scenario_creation")
class ScenarioCreationAPITests(TestCase):
    """Test scenario creation endpoint error handling."""

    @classmethod
    def setUpTestData(cls):
        """Set up test data: users and packages."""
        cls.user = UserManager.create_user(
            "scenario-test-user",
            "scenario-test@envipath.com",
            "SuperSafe",
            set_setting=False,
            add_to_group=False,
            is_active=True,
        )
        cls.other_user = UserManager.create_user(
            "other-user",
            "other@envipath.com",
            "SuperSafe",
            set_setting=False,
            add_to_group=False,
            is_active=True,
        )
        cls.package = PackageManager.create_package(
            cls.user, "Test Package", "Test package for scenario creation"
        )

    def test_create_scenario_package_not_found(self):
        """Test that non-existent package UUID returns 404."""
        self.client.force_login(self.user)

        fake_uuid = uuid4()
        payload = {
            "name": "Test Scenario",
            "description": "Test description",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [],
        }

        response = self.client.post(
            f"/api/v1/package/{fake_uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        self.assertEqual(response.status_code, 404)
        self.assertIn(f"Package with UUID {fake_uuid} not found", response.json()["detail"])

    def test_create_scenario_insufficient_permissions(self):
        """Test that unauthorized access returns 403."""
        self.client.force_login(self.other_user)

        payload = {
            "name": "Test Scenario",
            "description": "Test description",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [],
        }

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        self.assertEqual(response.status_code, 403)
        self.assertIn("permission", response.json()["detail"].lower())

    def test_create_scenario_invalid_ai_type(self):
        """Test that unknown additional information type returns 400."""
        self.client.force_login(self.user)

        payload = {
            "name": "Test Scenario",
            "description": "Test description",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [
                {"type": "invalid_type_that_does_not_exist", "data": {"some_field": "some_value"}}
            ],
        }

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        self.assertEqual(response.status_code, 400)
        response_data = response.json()
        self.assertIn("Validation errors", response_data["detail"])

    def test_create_scenario_validation_error(self):
        """Test that invalid additional information data returns 400."""
        self.client.force_login(self.user)

        # Use malformed data structure for an actual AI type
        payload = {
            "name": "Test Scenario",
            "description": "Test description",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [
                {
                    "type": "invalid_type_name",
                    "data": None,  # This should cause a validation error
                }
            ],
        }

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        # Should return 422 for validation errors
        self.assertEqual(response.status_code, 422)

    def test_create_scenario_success(self):
        """Test that valid scenario creation returns 200."""
        self.client.force_login(self.user)

        payload = {
            "name": "Test Scenario",
            "description": "Test description",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [],
        }

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        self.assertEqual(response.status_code, 200)
        data = response.json()
        self.assertEqual(data["name"], "Test Scenario")
        self.assertEqual(data["description"], "Test description")

        # Verify scenario was actually created
        scenario = Scenario.objects.get(name="Test Scenario")
        self.assertEqual(scenario.package, self.package)
        self.assertEqual(scenario.scenario_type, "biodegradation")

    def test_create_scenario_auto_name(self):
        """Test that empty name triggers auto-generation."""
        self.client.force_login(self.user)

        payload = {
            "name": "",  # Empty name should be auto-generated
            "description": "Test description",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [],
        }

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        self.assertEqual(response.status_code, 200)
        data = response.json()
        # Auto-generated name should follow pattern "Scenario N"
        self.assertTrue(data["name"].startswith("Scenario "))

    def test_create_scenario_xss_protection(self):
        """Test that XSS attempts are sanitized."""
        self.client.force_login(self.user)

        payload = {
            "name": "<script>alert('xss')</script>Clean Name",
            "description": "<img src=x onerror=alert('xss')>Description",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [],
        }

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        self.assertEqual(response.status_code, 200)
        data = response.json()
        # XSS should be cleaned out
        self.assertNotIn("<script>", data["name"])
        self.assertNotIn("onerror", data["description"])

    def test_create_scenario_missing_required_field(self):
        """Test that missing required fields returns validation error."""
        self.client.force_login(self.user)

        # Missing 'name' field entirely
        payload = {
            "description": "Test description",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [],
        }

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        # Should return 422 for schema validation errors
        self.assertEqual(response.status_code, 422)

    def test_create_scenario_type_error_in_ai(self):
        """Test that TypeError in AI instantiation returns 400."""
        self.client.force_login(self.user)

        payload = {
            "name": "Test Scenario",
            "description": "Test description",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [
                {
                    "type": "invalid_type_name",
                    "data": "string instead of dict",  # Wrong type
                }
            ],
        }

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        # Should return 422 for validation errors
        self.assertEqual(response.status_code, 422)

    def test_create_scenario_default_values(self):
        """Test that default values are applied correctly."""
        self.client.force_login(self.user)

        # Minimal payload with only name
        payload = {"name": "Minimal Scenario"}

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        self.assertEqual(response.status_code, 200)
        data = response.json()
        self.assertEqual(data["name"], "Minimal Scenario")
        # Check defaults are applied
        scenario = Scenario.objects.get(name="Minimal Scenario")
        # Default description from model is "no description"
        self.assertIn(scenario.description.lower(), ["", "no description"])

    def test_create_scenario_unicode_characters(self):
        """Test that unicode characters are handled properly."""
        self.client.force_login(self.user)

        payload = {
            "name": "Test Scenario 测试 🧪",
            "description": "Description with émojis and spëcial çhars",
            "scenario_date": "2024-01-01",
            "scenario_type": "biodegradation",
            "additional_information": [],
        }

        response = self.client.post(
            f"/api/v1/package/{self.package.uuid}/scenario/",
            data=json.dumps(payload),
            content_type="application/json",
        )

        self.assertEqual(response.status_code, 200)
        data = response.json()
        self.assertIn("测试", data["name"])
        self.assertIn("émojis", data["description"])
