"""
Combat API Blueprint

This module provides API endpoints for turn-based combat:
- Starting combat encounters
- Executing combat actions (attack, ability, defend, flee)
- Getting combat state
- Processing enemy turns
"""

from flask import Blueprint, request

from app.services.combat_service import (
    get_combat_service,
    CombatAction,
    CombatError,
    NotInCombatError,
    AlreadyInCombatError,
    InvalidActionError,
    InsufficientResourceError,
)
from app.models.enums import CombatStatus
from app.utils.response import (
    success_response,
    error_response,
    not_found_response,
    validation_error_response
)
from app.utils.auth import require_auth, get_current_user
from app.utils.logging import get_logger


# Initialize logger
logger = get_logger(__file__)

# Create blueprint
combat_bp = Blueprint('combat', __name__, url_prefix='/api/v1/combat')


# =============================================================================
# Combat Lifecycle Endpoints
# =============================================================================

@combat_bp.route('/start', methods=['POST'])
@require_auth
def start_combat():
    """
    Start a new combat encounter.

    Creates a combat encounter with the session's character(s) vs specified enemies.
    Rolls initiative and sets up turn order.

    Request JSON:
        {
            "session_id": "sess_123",
            "enemy_ids": ["goblin", "goblin", "goblin_shaman"]
        }

    Returns:
        {
            "encounter_id": "enc_abc123",
            "combatants": [...],
            "turn_order": [...],
            "current_turn": "char_456",
            "round_number": 1,
            "status": "active"
        }

    Errors:
        400: Missing required fields
        400: Already in combat
        404: Enemy template not found
    """
    user = get_current_user()
    data = request.get_json()

    if not data:
        return validation_error_response(
            message="Request body is required",
            details={"field": "body", "issue": "Missing JSON body"}
        )

    # Validate required fields
    session_id = data.get("session_id")
    enemy_ids = data.get("enemy_ids", [])

    if not session_id:
        return validation_error_response(
            message="session_id is required",
            details={"field": "session_id", "issue": "Missing required field"}
        )

    if not enemy_ids:
        return validation_error_response(
            message="enemy_ids is required and must not be empty",
            details={"field": "enemy_ids", "issue": "Missing or empty list"}
        )

    try:
        combat_service = get_combat_service()
        encounter = combat_service.start_combat(
            session_id=session_id,
            user_id=user["user_id"],
            enemy_ids=enemy_ids,
        )

        # Format response
        current = encounter.get_current_combatant()
        response_data = {
            "encounter_id": encounter.encounter_id,
            "combatants": [
                {
                    "combatant_id": c.combatant_id,
                    "name": c.name,
                    "is_player": c.is_player,
                    "current_hp": c.current_hp,
                    "max_hp": c.max_hp,
                    "current_mp": c.current_mp,
                    "max_mp": c.max_mp,
                    "initiative": c.initiative,
                    "abilities": c.abilities,
                }
                for c in encounter.combatants
            ],
            "turn_order": encounter.turn_order,
            "current_turn": current.combatant_id if current else None,
            "round_number": encounter.round_number,
            "status": encounter.status.value,
        }

        logger.info("Combat started via API",
                   session_id=session_id,
                   encounter_id=encounter.encounter_id,
                   enemy_count=len(enemy_ids))

        return success_response(response_data)

    except AlreadyInCombatError as e:
        logger.warning("Attempt to start combat while already in combat",
                      session_id=session_id)
        return error_response(
            status_code=400,
            message=str(e),
            error_code="ALREADY_IN_COMBAT"
        )
    except ValueError as e:
        logger.warning("Invalid enemy ID",
                      session_id=session_id,
                      error=str(e))
        return not_found_response(message=str(e))
    except Exception as e:
        logger.error("Failed to start combat",
                    session_id=session_id,
                    error=str(e),
                    exc_info=True)
        return error_response(
            status_code=500,
            message="Failed to start combat",
            error_code="COMBAT_START_ERROR"
        )


@combat_bp.route('/<session_id>/state', methods=['GET'])
@require_auth
def get_combat_state(session_id: str):
    """
    Get current combat state for a session.

    Returns the full combat encounter state including all combatants,
    turn order, combat log, and current status.

    Path Parameters:
        session_id: Game session ID

    Returns:
        {
            "in_combat": true,
            "encounter": {
                "encounter_id": "...",
                "combatants": [...],
                "turn_order": [...],
                "current_turn": "...",
                "round_number": 1,
                "status": "active",
                "combat_log": [...]
            }
        }

        or if not in combat:
        {
            "in_combat": false,
            "encounter": null
        }
    """
    user = get_current_user()

    try:
        combat_service = get_combat_service()
        encounter = combat_service.get_combat_state(session_id, user["user_id"])

        if not encounter:
            return success_response({
                "in_combat": False,
                "encounter": None
            })

        current = encounter.get_current_combatant()
        response_data = {
            "in_combat": True,
            "encounter": {
                "encounter_id": encounter.encounter_id,
                "combatants": [
                    {
                        "combatant_id": c.combatant_id,
                        "name": c.name,
                        "is_player": c.is_player,
                        "current_hp": c.current_hp,
                        "max_hp": c.max_hp,
                        "current_mp": c.current_mp,
                        "max_mp": c.max_mp,
                        "is_alive": c.is_alive(),
                        "is_stunned": c.is_stunned(),
                        "active_effects": [
                            {"name": e.name, "duration": e.duration}
                            for e in c.active_effects
                        ],
                        "abilities": c.abilities,
                        "cooldowns": c.cooldowns,
                    }
                    for c in encounter.combatants
                ],
                "turn_order": encounter.turn_order,
                "current_turn": current.combatant_id if current else None,
                "round_number": encounter.round_number,
                "status": encounter.status.value,
                "combat_log": encounter.combat_log[-10:],  # Last 10 entries
            }
        }

        return success_response(response_data)

    except Exception as e:
        logger.error("Failed to get combat state",
                    session_id=session_id,
                    error=str(e),
                    exc_info=True)
        return error_response(
            status_code=500,
            message="Failed to get combat state",
            error_code="COMBAT_STATE_ERROR"
        )


# =============================================================================
# Action Execution Endpoints
# =============================================================================

@combat_bp.route('/<session_id>/action', methods=['POST'])
@require_auth
def execute_action(session_id: str):
    """
    Execute a combat action for a combatant.

    Processes the specified action (attack, ability, defend, flee, item)
    for the given combatant. Must be that combatant's turn.

    Path Parameters:
        session_id: Game session ID

    Request JSON:
        {
            "combatant_id": "char_456",
            "action_type": "attack" | "ability" | "defend" | "flee" | "item",
            "target_ids": ["enemy_1"],  // Optional, auto-targets if omitted
            "ability_id": "fireball",   // Required for ability actions
            "item_id": "health_potion"  // Required for item actions
        }

    Returns:
        {
            "success": true,
            "message": "Attack hits for 15 damage!",
            "damage_results": [...],
            "effects_applied": [...],
            "combat_ended": false,
            "combat_status": null,
            "next_combatant_id": "goblin_0"
        }

    Errors:
        400: Missing required fields
        400: Not this combatant's turn
        400: Invalid action
        404: Session not in combat
    """
    user = get_current_user()
    data = request.get_json()

    if not data:
        return validation_error_response(
            message="Request body is required",
            details={"field": "body", "issue": "Missing JSON body"}
        )

    # Validate required fields
    combatant_id = data.get("combatant_id")
    action_type = data.get("action_type")

    if not combatant_id:
        return validation_error_response(
            message="combatant_id is required",
            details={"field": "combatant_id", "issue": "Missing required field"}
        )

    if not action_type:
        return validation_error_response(
            message="action_type is required",
            details={"field": "action_type", "issue": "Missing required field"}
        )

    valid_actions = ["attack", "ability", "defend", "flee", "item"]
    if action_type not in valid_actions:
        return validation_error_response(
            message=f"Invalid action_type. Must be one of: {valid_actions}",
            details={"field": "action_type", "issue": "Invalid value"}
        )

    # Validate ability_id for ability actions
    if action_type == "ability" and not data.get("ability_id"):
        return validation_error_response(
            message="ability_id is required for ability actions",
            details={"field": "ability_id", "issue": "Missing required field"}
        )

    try:
        combat_service = get_combat_service()

        action = CombatAction(
            action_type=action_type,
            target_ids=data.get("target_ids", []),
            ability_id=data.get("ability_id"),
            item_id=data.get("item_id"),
        )

        result = combat_service.execute_action(
            session_id=session_id,
            user_id=user["user_id"],
            combatant_id=combatant_id,
            action=action,
        )

        logger.info("Combat action executed",
                   session_id=session_id,
                   combatant_id=combatant_id,
                   action_type=action_type,
                   success=result.success)

        return success_response(result.to_dict())

    except NotInCombatError as e:
        logger.warning("Action attempted while not in combat",
                      session_id=session_id)
        return not_found_response(message="Session is not in combat")
    except InvalidActionError as e:
        logger.warning("Invalid combat action",
                      session_id=session_id,
                      combatant_id=combatant_id,
                      error=str(e))
        return error_response(
            status_code=400,
            message=str(e),
            error_code="INVALID_ACTION"
        )
    except InsufficientResourceError as e:
        logger.warning("Insufficient resources for action",
                      session_id=session_id,
                      combatant_id=combatant_id,
                      error=str(e))
        return error_response(
            status_code=400,
            message=str(e),
            error_code="INSUFFICIENT_RESOURCES"
        )
    except Exception as e:
        logger.error("Failed to execute combat action",
                    session_id=session_id,
                    combatant_id=combatant_id,
                    action_type=action_type,
                    error=str(e),
                    exc_info=True)
        return error_response(
            status_code=500,
            message="Failed to execute action",
            error_code="ACTION_EXECUTION_ERROR"
        )


@combat_bp.route('/<session_id>/enemy-turn', methods=['POST'])
@require_auth
def execute_enemy_turn(session_id: str):
    """
    Execute the current enemy's turn using AI logic.

    Called when it's an enemy combatant's turn. The enemy AI will
    automatically choose and execute an appropriate action.

    Path Parameters:
        session_id: Game session ID

    Returns:
        {
            "success": true,
            "message": "Goblin attacks Hero for 8 damage!",
            "damage_results": [...],
            "effects_applied": [...],
            "combat_ended": false,
            "combat_status": null,
            "next_combatant_id": "char_456"
        }

    Errors:
        400: Current combatant is not an enemy
        404: Session not in combat
    """
    user = get_current_user()

    try:
        combat_service = get_combat_service()
        result = combat_service.execute_enemy_turn(
            session_id=session_id,
            user_id=user["user_id"],
        )

        logger.info("Enemy turn executed",
                   session_id=session_id,
                   success=result.success)

        return success_response(result.to_dict())

    except NotInCombatError as e:
        return not_found_response(message="Session is not in combat")
    except InvalidActionError as e:
        return error_response(
            status_code=400,
            message=str(e),
            error_code="INVALID_ACTION"
        )
    except Exception as e:
        logger.error("Failed to execute enemy turn",
                    session_id=session_id,
                    error=str(e),
                    exc_info=True)
        return error_response(
            status_code=500,
            message="Failed to execute enemy turn",
            error_code="ENEMY_TURN_ERROR"
        )


@combat_bp.route('/<session_id>/flee', methods=['POST'])
@require_auth
def attempt_flee(session_id: str):
    """
    Attempt to flee from combat.

    The current combatant attempts to flee. Success is based on
    DEX comparison with enemies. Failed flee attempts consume the turn.

    Path Parameters:
        session_id: Game session ID

    Request JSON:
        {
            "combatant_id": "char_456"
        }

    Returns:
        {
            "success": true,
            "message": "Successfully fled from combat!",
            "combat_ended": true,
            "combat_status": "fled"
        }

        or on failure:
        {
            "success": false,
            "message": "Failed to flee! (Roll: 0.35, Needed: 0.50)",
            "combat_ended": false
        }
    """
    user = get_current_user()
    data = request.get_json() or {}

    combatant_id = data.get("combatant_id")

    try:
        combat_service = get_combat_service()

        action = CombatAction(
            action_type="flee",
            target_ids=[],
        )

        result = combat_service.execute_action(
            session_id=session_id,
            user_id=user["user_id"],
            combatant_id=combatant_id,
            action=action,
        )

        return success_response(result.to_dict())

    except NotInCombatError:
        return not_found_response(message="Session is not in combat")
    except InvalidActionError as e:
        return error_response(
            status_code=400,
            message=str(e),
            error_code="INVALID_ACTION"
        )
    except Exception as e:
        logger.error("Failed flee attempt",
                    session_id=session_id,
                    error=str(e),
                    exc_info=True)
        return error_response(
            status_code=500,
            message="Failed to attempt flee",
            error_code="FLEE_ERROR"
        )


@combat_bp.route('/<session_id>/end', methods=['POST'])
@require_auth
def end_combat(session_id: str):
    """
    Force end the current combat (debug/admin endpoint).

    Ends combat with the specified outcome. Should normally only be used
    for debugging or admin purposes - combat usually ends automatically.

    Path Parameters:
        session_id: Game session ID

    Request JSON:
        {
            "outcome": "victory" | "defeat" | "fled"
        }

    Returns:
        {
            "outcome": "victory",
            "rewards": {
                "experience": 100,
                "gold": 50,
                "items": [...],
                "level_ups": []
            }
        }
    """
    user = get_current_user()
    data = request.get_json() or {}

    outcome_str = data.get("outcome", "fled")

    # Parse outcome
    try:
        outcome = CombatStatus(outcome_str)
    except ValueError:
        return validation_error_response(
            message="Invalid outcome. Must be: victory, defeat, or fled",
            details={"field": "outcome", "issue": "Invalid value"}
        )

    try:
        combat_service = get_combat_service()
        rewards = combat_service.end_combat(
            session_id=session_id,
            user_id=user["user_id"],
            outcome=outcome,
        )

        logger.info("Combat force-ended",
                   session_id=session_id,
                   outcome=outcome_str)

        return success_response({
            "outcome": outcome_str,
            "rewards": rewards.to_dict() if outcome == CombatStatus.VICTORY else None,
        })

    except NotInCombatError:
        return not_found_response(message="Session is not in combat")
    except Exception as e:
        logger.error("Failed to end combat",
                    session_id=session_id,
                    error=str(e),
                    exc_info=True)
        return error_response(
            status_code=500,
            message="Failed to end combat",
            error_code="COMBAT_END_ERROR"
        )


# =============================================================================
# Utility Endpoints
# =============================================================================

@combat_bp.route('/enemies', methods=['GET'])
def list_enemies():
    """
    List all available enemy templates.

    Returns a list of all enemy templates that can be used in combat.
    Useful for encounter building and testing.

    Query Parameters:
        difficulty: Filter by difficulty (easy, medium, hard, boss)
        tag: Filter by tag (undead, beast, humanoid, etc.)

    Returns:
        {
            "enemies": [
                {
                    "enemy_id": "goblin",
                    "name": "Goblin Scout",
                    "difficulty": "easy",
                    "tags": ["humanoid", "goblinoid"],
                    "experience_reward": 15
                },
                ...
            ]
        }
    """
    from app.services.enemy_loader import get_enemy_loader
    from app.models.enemy import EnemyDifficulty

    difficulty = request.args.get("difficulty")
    tag = request.args.get("tag")

    try:
        enemy_loader = get_enemy_loader()
        enemy_loader.load_all_enemies()

        if difficulty:
            try:
                diff = EnemyDifficulty(difficulty)
                enemies = enemy_loader.get_enemies_by_difficulty(diff)
            except ValueError:
                return validation_error_response(
                    message="Invalid difficulty",
                    details={"field": "difficulty", "issue": "Must be: easy, medium, hard, boss"}
                )
        elif tag:
            enemies = enemy_loader.get_enemies_by_tag(tag)
        else:
            enemies = list(enemy_loader.get_all_cached().values())

        response_data = {
            "enemies": [
                {
                    "enemy_id": e.enemy_id,
                    "name": e.name,
                    "description": e.description[:100] + "..." if len(e.description) > 100 else e.description,
                    "difficulty": e.difficulty.value,
                    "tags": e.tags,
                    "experience_reward": e.experience_reward,
                    "gold_reward_range": [e.gold_reward_min, e.gold_reward_max],
                }
                for e in enemies
            ]
        }

        return success_response(response_data)

    except Exception as e:
        logger.error("Failed to list enemies",
                    error=str(e),
                    exc_info=True)
        return error_response(
            status_code=500,
            message="Failed to list enemies",
            error_code="ENEMY_LIST_ERROR"
        )


@combat_bp.route('/enemies/<enemy_id>', methods=['GET'])
def get_enemy_details(enemy_id: str):
    """
    Get detailed information about a specific enemy template.

    Path Parameters:
        enemy_id: Enemy template ID

    Returns:
        {
            "enemy_id": "goblin",
            "name": "Goblin Scout",
            "description": "...",
            "base_stats": {...},
            "abilities": [...],
            "loot_table": [...],
            "difficulty": "easy",
            ...
        }
    """
    from app.services.enemy_loader import get_enemy_loader

    try:
        enemy_loader = get_enemy_loader()
        enemy = enemy_loader.load_enemy(enemy_id)

        if not enemy:
            return not_found_response(message=f"Enemy not found: {enemy_id}")

        return success_response(enemy.to_dict())

    except Exception as e:
        logger.error("Failed to get enemy details",
                    enemy_id=enemy_id,
                    error=str(e),
                    exc_info=True)
        return error_response(
            status_code=500,
            message="Failed to get enemy details",
            error_code="ENEMY_DETAILS_ERROR"
        )
