"""
Integration tests for Inventory API endpoints.

Tests the REST API endpoints for inventory management functionality.
"""

import pytest
from unittest.mock import Mock, patch, MagicMock
from flask import Flask
import json

from app import create_app
from app.api.inventory import inventory_bp
from app.models.items import Item
from app.models.character import Character
from app.models.stats import Stats
from app.models.skills import PlayerClass
from app.models.origins import Origin
from app.models.enums import ItemType, ItemRarity, DamageType
from app.services.inventory_service import (
    InventoryService,
    ItemNotFoundError,
    CannotEquipError,
    InvalidSlotError,
    CannotUseItemError,
    InventoryFullError,
    VALID_SLOTS,
)
from app.services.character_service import CharacterNotFound


# =============================================================================
# Test Fixtures
# =============================================================================

@pytest.fixture
def app():
    """Create test Flask application."""
    app = create_app('development')
    app.config['TESTING'] = True
    return app


@pytest.fixture
def client(app):
    """Create test client."""
    return app.test_client()


@pytest.fixture
def sample_stats():
    """Sample stats for testing."""
    return Stats(
        strength=12,
        dexterity=14,
        constitution=10,
        intelligence=10,
        wisdom=10,
        charisma=10,
        luck=10
    )


@pytest.fixture
def sample_weapon():
    """Sample weapon item."""
    return Item(
        item_id="test_sword_001",
        name="Iron Sword",
        item_type=ItemType.WEAPON,
        rarity=ItemRarity.COMMON,
        description="A sturdy iron sword",
        value=50,
        damage=8,
        damage_type=DamageType.PHYSICAL,
        crit_chance=0.05,
        crit_multiplier=2.0,
        required_level=1,
    )


@pytest.fixture
def sample_armor():
    """Sample armor item."""
    return Item(
        item_id="test_helmet_001",
        name="Iron Helmet",
        item_type=ItemType.ARMOR,
        rarity=ItemRarity.COMMON,
        description="A sturdy iron helmet",
        value=30,
        defense=5,
        resistance=2,
        required_level=1,
    )


@pytest.fixture
def sample_consumable():
    """Sample consumable item."""
    return Item(
        item_id="health_potion_small",
        name="Small Health Potion",
        item_type=ItemType.CONSUMABLE,
        rarity=ItemRarity.COMMON,
        description="Restores a small amount of health",
        value=25,
        effects_on_use=[],  # Simplified for testing
    )


@pytest.fixture
def sample_class():
    """Sample player class."""
    return PlayerClass(
        class_id="vanguard",
        name="Vanguard",
        description="A heavily armored warrior",
        base_stats=Stats(
            strength=14,
            dexterity=10,
            constitution=14,
            intelligence=8,
            wisdom=8,
            charisma=10,
            luck=10
        ),
        skill_trees=[],
        starting_equipment=[],
        starting_abilities=[],
    )


@pytest.fixture
def sample_origin():
    """Sample origin."""
    return Origin(
        id="soul_revenant",
        name="Soul Revenant",
        description="Returned from death",
        starting_location={"area": "graveyard", "name": "Graveyard"},
        narrative_hooks=[],
        starting_bonus={},
    )


@pytest.fixture
def sample_character(sample_class, sample_origin, sample_weapon, sample_armor, sample_consumable):
    """Sample character with inventory."""
    char = Character(
        character_id="test_char_001",
        user_id="test_user_001",
        name="Test Hero",
        player_class=sample_class,
        origin=sample_origin,
        level=5,
        experience=0,
        gold=100,
        inventory=[sample_weapon, sample_armor, sample_consumable],
        equipped={},
        unlocked_skills=[],
    )
    return char


# =============================================================================
# GET Inventory Endpoint Tests
# =============================================================================

class TestGetInventoryEndpoint:
    """Tests for GET /api/v1/characters/<id>/inventory endpoint."""

    def test_get_inventory_requires_auth(self, client):
        """Test that inventory endpoint requires authentication."""
        response = client.get('/api/v1/characters/test_char_001/inventory')

        # Should return 401 Unauthorized without valid session
        assert response.status_code == 401

    def test_get_inventory_character_not_found(self, client):
        """Test getting inventory for non-existent character returns 404 (after auth)."""
        # Without auth, returns 401 regardless
        response = client.get('/api/v1/characters/nonexistent_12345/inventory')

        assert response.status_code == 401


# =============================================================================
# POST Equip Endpoint Tests
# =============================================================================

class TestEquipEndpoint:
    """Tests for POST /api/v1/characters/<id>/inventory/equip endpoint."""

    def test_equip_requires_auth(self, client):
        """Test that equip endpoint requires authentication."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/equip',
            json={
                'item_id': 'test_sword_001',
                'slot': 'weapon'
            }
        )

        # Should return 401 Unauthorized without valid session
        assert response.status_code == 401

    def test_equip_missing_item_id(self, client):
        """Test equip without item_id still requires auth first."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/equip',
            json={'slot': 'weapon'}
        )

        # Without auth, returns 401 regardless of payload issues
        assert response.status_code == 401

    def test_equip_missing_slot(self, client):
        """Test equip without slot still requires auth first."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/equip',
            json={'item_id': 'test_sword_001'}
        )

        # Without auth, returns 401 regardless of payload issues
        assert response.status_code == 401

    def test_equip_missing_body(self, client):
        """Test equip without request body still requires auth first."""
        response = client.post('/api/v1/characters/test_char_001/inventory/equip')

        # Without auth, returns 401 regardless of payload issues
        assert response.status_code == 401


# =============================================================================
# POST Unequip Endpoint Tests
# =============================================================================

class TestUnequipEndpoint:
    """Tests for POST /api/v1/characters/<id>/inventory/unequip endpoint."""

    def test_unequip_requires_auth(self, client):
        """Test that unequip endpoint requires authentication."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/unequip',
            json={'slot': 'weapon'}
        )

        # Should return 401 Unauthorized without valid session
        assert response.status_code == 401

    def test_unequip_missing_slot(self, client):
        """Test unequip without slot still requires auth first."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/unequip',
            json={}
        )

        # Without auth, returns 401 regardless of payload issues
        assert response.status_code == 401

    def test_unequip_missing_body(self, client):
        """Test unequip without request body still requires auth first."""
        response = client.post('/api/v1/characters/test_char_001/inventory/unequip')

        # Without auth, returns 401 regardless of payload issues
        assert response.status_code == 401


# =============================================================================
# POST Use Item Endpoint Tests
# =============================================================================

class TestUseItemEndpoint:
    """Tests for POST /api/v1/characters/<id>/inventory/use endpoint."""

    def test_use_requires_auth(self, client):
        """Test that use item endpoint requires authentication."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/use',
            json={'item_id': 'health_potion_small'}
        )

        # Should return 401 Unauthorized without valid session
        assert response.status_code == 401

    def test_use_missing_item_id(self, client):
        """Test use item without item_id still requires auth first."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/use',
            json={}
        )

        # Without auth, returns 401 regardless of payload issues
        assert response.status_code == 401

    def test_use_missing_body(self, client):
        """Test use item without request body still requires auth first."""
        response = client.post('/api/v1/characters/test_char_001/inventory/use')

        # Without auth, returns 401 regardless of payload issues
        assert response.status_code == 401


# =============================================================================
# DELETE Drop Item Endpoint Tests
# =============================================================================

class TestDropItemEndpoint:
    """Tests for DELETE /api/v1/characters/<id>/inventory/<item_id> endpoint."""

    def test_drop_requires_auth(self, client):
        """Test that drop item endpoint requires authentication."""
        response = client.delete(
            '/api/v1/characters/test_char_001/inventory/test_sword_001'
        )

        # Should return 401 Unauthorized without valid session
        assert response.status_code == 401


# =============================================================================
# Valid Slot Tests (Unit level)
# =============================================================================

class TestValidSlots:
    """Tests to verify slot configuration."""

    def test_valid_slots_defined(self):
        """Test that all expected slots are defined."""
        expected_slots = {
            'weapon', 'off_hand', 'helmet', 'chest',
            'gloves', 'boots', 'accessory_1', 'accessory_2'
        }
        assert VALID_SLOTS == expected_slots

    def test_valid_slots_count(self):
        """Test that we have exactly 8 equipment slots."""
        assert len(VALID_SLOTS) == 8


# =============================================================================
# Endpoint URL Pattern Tests
# =============================================================================

class TestEndpointURLPatterns:
    """Tests to verify correct URL patterns."""

    def test_get_inventory_url(self, client):
        """Test GET inventory URL pattern."""
        response = client.get('/api/v1/characters/any_id/inventory')
        # Should be 401 (auth required), not 404 (route not found)
        assert response.status_code == 401

    def test_equip_url(self, client):
        """Test POST equip URL pattern."""
        response = client.post(
            '/api/v1/characters/any_id/inventory/equip',
            json={'item_id': 'x', 'slot': 'weapon'}
        )
        # Should be 401 (auth required), not 404 (route not found)
        assert response.status_code == 401

    def test_unequip_url(self, client):
        """Test POST unequip URL pattern."""
        response = client.post(
            '/api/v1/characters/any_id/inventory/unequip',
            json={'slot': 'weapon'}
        )
        # Should be 401 (auth required), not 404 (route not found)
        assert response.status_code == 401

    def test_use_url(self, client):
        """Test POST use URL pattern."""
        response = client.post(
            '/api/v1/characters/any_id/inventory/use',
            json={'item_id': 'x'}
        )
        # Should be 401 (auth required), not 404 (route not found)
        assert response.status_code == 401

    def test_drop_url(self, client):
        """Test DELETE drop URL pattern."""
        response = client.delete('/api/v1/characters/any_id/inventory/item_123')
        # Should be 401 (auth required), not 404 (route not found)
        assert response.status_code == 401


# =============================================================================
# Response Format Tests (verifying blueprint registration)
# =============================================================================

class TestResponseFormats:
    """Tests to verify API response format consistency."""

    def test_get_inventory_401_format(self, client):
        """Test that 401 response follows standard format."""
        response = client.get('/api/v1/characters/test_char_001/inventory')

        assert response.status_code == 401
        data = response.get_json()

        # Standard response format should include status
        assert data is not None
        assert 'status' in data
        assert data['status'] == 401

    def test_equip_401_format(self, client):
        """Test that equip 401 response follows standard format."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/equip',
            json={'item_id': 'test', 'slot': 'weapon'}
        )

        assert response.status_code == 401
        data = response.get_json()

        assert data is not None
        assert 'status' in data
        assert data['status'] == 401

    def test_unequip_401_format(self, client):
        """Test that unequip 401 response follows standard format."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/unequip',
            json={'slot': 'weapon'}
        )

        assert response.status_code == 401
        data = response.get_json()

        assert data is not None
        assert 'status' in data
        assert data['status'] == 401

    def test_use_401_format(self, client):
        """Test that use 401 response follows standard format."""
        response = client.post(
            '/api/v1/characters/test_char_001/inventory/use',
            json={'item_id': 'test'}
        )

        assert response.status_code == 401
        data = response.get_json()

        assert data is not None
        assert 'status' in data
        assert data['status'] == 401

    def test_drop_401_format(self, client):
        """Test that drop 401 response follows standard format."""
        response = client.delete(
            '/api/v1/characters/test_char_001/inventory/test_item'
        )

        assert response.status_code == 401
        data = response.get_json()

        assert data is not None
        assert 'status' in data
        assert data['status'] == 401
