"""Routes pour le gameplay (génération et interaction)."""

from flask import Blueprint, request, jsonify
from flask_jwt_extended import jwt_required, get_jwt_identity
from pydantic import ValidationError, BaseModel, Field
from typing import Optional

from services.story_service import StoryService
from services.ai_service import AIService
from services.lorebook_service import LoreBookService
from models.user import db
from models.story_turn import StoryTurn

bp = Blueprint('gameplay', __name__)


class InitStorySchema(BaseModel):
    """Schéma pour initialiser une histoire (skeleton + cover)."""
    story_id: str
    context_answers: Optional[dict] = None


class StartStorySchema(BaseModel):
    """Schéma pour démarrer la narration."""
    story_id: str


class ActionSchema(BaseModel):
    """Schéma pour envoyer une action du joueur."""
    story_id: str
    action: str
    is_technical: bool = Field(default=False)
    selected_choice_index: Optional[int] = None


@bp.route('/init-story', methods=['POST'])
@jwt_required()
def init_story() -> tuple:
    """Initialise une histoire : génère le skeleton et la couverture.
    
    Headers:
        Authorization: Bearer <access_token>
    
    Body (JSON):
        story_id: ID de l'histoire à initialiser.
        context_answers: Réponses aux questions de contextualisation (optionnel).
    
    Returns:
        Tuple (response, status_code).
    """
    try:
        user_id = get_jwt_identity()
        data = InitStorySchema(**request.get_json())
        
        story = StoryService.get_story_by_id(data.story_id)
        
        if not story:
            return jsonify({
                'error': 'story_not_found',
                'message': 'Histoire non trouvée'
            }), 404
        
        if story.user_id != user_id:
            return jsonify({
                'error': 'forbidden',
                'message': 'Vous n\'avez pas accès à cette histoire'
            }), 403
        
        # Sauvegarder les réponses de contextualisation
        if data.context_answers:
            story.context_answers = data.context_answers
        
        # Générer le squelette narratif
        skeleton = AIService.generate_narrative_skeleton(
            genre=story.genre or "aventure",
            epoch=story.epoch or "contemporain",
            reality_level=story.reality_level,
            context_answers=story.context_answers,
            story_id=story.id,
            pitch=story.pitch
        )
        story.narrative_skeleton = skeleton
        
        # Générer la couverture
        cover_url = AIService.generate_cover_image(
            title=story.title,
            genre=story.genre or "aventure",
            epoch=story.epoch or "contemporain",
            skeleton=skeleton,
            story_id=story.id,
            pitch=story.pitch
        )
        story.cover_image_url = cover_url
        
        db.session.commit()
        
        return jsonify({
            'message': 'Histoire initialisée avec succès',
            'story': story.to_dict()
        }), 200
        
    except ValidationError as e:
        return jsonify({
            'error': 'validation_error',
            'message': 'Données invalides',
            'details': e.errors()
        }), 400
        
    except Exception as e:
        db.session.rollback()
        return jsonify({
            'error': 'server_error',
            'message': f'Une erreur est survenue : {str(e)}'
        }), 500


@bp.route('/start-story/<story_id>', methods=['POST'])
@jwt_required()
def start_story(story_id: str) -> tuple:
    """Génère le premier passage narratif de l'histoire.
    
    Headers:
        Authorization: Bearer <access_token>
    
    Args:
        story_id: ID de l'histoire.
    
    Returns:
        Tuple (response, status_code).
    """
    try:
        user_id = get_jwt_identity()
        
        story = StoryService.get_story_by_id(story_id)
        
        if not story:
            return jsonify({
                'error': 'story_not_found',
                'message': 'Histoire non trouvée'
            }), 404
        
        if story.user_id != user_id:
            return jsonify({
                'error': 'forbidden',
                'message': 'Vous n\'avez pas accès à cette histoire'
            }), 403
        
        # Vérifier que l'histoire est initialisée
        if not story.narrative_skeleton:
            return jsonify({
                'error': 'story_not_initialized',
                'message': 'L\'histoire doit d\'abord être initialisée (skeleton + cover)'
            }), 400
        
        # Vérifier qu'elle n'a pas déjà commencé
        if story.turn_count > 0:
            return jsonify({
                'error': 'story_already_started',
                'message': 'L\'histoire a déjà commencé'
            }), 400
        
        # Générer le premier passage
        narrative_text = AIService.generate_narrative_passage(
            story=story,
            lorebook=story.lorebook,
            recent_turns=[],
            player_action=None,
            is_technical=False,
            is_first_passage=True
        )
        
        # Générer les choix si mode choices
        available_choices = None
        if story.game_mode == 'choices':
            protagonist_traits = story.lorebook.protagonist_state.get('traits', []) if story.lorebook else []
            available_choices = AIService.generate_choices(
                story=story,
                narrative_passage=narrative_text,
                protagonist_traits=protagonist_traits
            )
        
        # Créer le premier turn
        turn = StoryTurn(
            story_id=story.id,
            turn_number=1,
            narrative_text=narrative_text,
            available_choices=available_choices,
            is_published=False
        )
        
        db.session.add(turn)
        story.turn_count = 1
        db.session.commit()
        
        # Mettre à jour le LoreBook avec le premier passage
        if LoreBookService.should_update_lorebook(narrative_text):
            LoreBookService.update_lorebook(
                lorebook=story.lorebook,
                narrative_text=narrative_text,
                player_action=None,
                turn_number=1
            )
        
        return jsonify({
            'message': 'Histoire démarrée',
            'turn': turn.to_dict(),
            'story': story.to_dict()
        }), 200
        
    except Exception as e:
        db.session.rollback()
        return jsonify({
            'error': 'server_error',
            'message': f'Une erreur est survenue : {str(e)}'
        }), 500


@bp.route('/action', methods=['POST'])
@jwt_required()
def player_action() -> tuple:
    """Traite une action du joueur et génère la suite.
    
    Headers:
        Authorization: Bearer <access_token>
    
    Body (JSON):
        story_id: ID de l'histoire.
        action: Texte de l'action du joueur.
        is_technical: Si True, c'est un message OFF (défaut: False).
        selected_choice_index: Index du choix sélectionné si mode choices (0-3).
    
    Returns:
        Tuple (response, status_code).
    """
    try:
        user_id = get_jwt_identity()
        data = ActionSchema(**request.get_json())
        
        story = StoryService.get_story_by_id(data.story_id)
        
        if not story:
            return jsonify({
                'error': 'story_not_found',
                'message': 'Histoire non trouvée'
            }), 404
        
        if story.user_id != user_id:
            return jsonify({
                'error': 'forbidden',
                'message': 'Vous n\'avez pas accès à cette histoire'
            }), 403
        
        if story.turn_count == 0:
            return jsonify({
                'error': 'story_not_started',
                'message': 'L\'histoire n\'a pas encore commencé'
            }), 400
        
        # Récupérer les derniers turns pour le contexte
        recent_turns = StoryTurn.query.filter_by(story_id=story.id)\
            .order_by(StoryTurn.turn_number.desc())\
            .limit(5)\
            .all()
        recent_turns.reverse()  # Remettre dans l'ordre chronologique
        
        # Générer le nouveau passage narratif
        narrative_text = AIService.generate_narrative_passage(
            story=story,
            lorebook=story.lorebook,
            recent_turns=recent_turns,
            player_action=data.action,
            is_technical=data.is_technical,
            is_first_passage=False
        )
        
        # Générer les nouveaux choix si mode choices et pas de message OFF
        available_choices = None
        if story.game_mode == 'choices' and not data.is_technical:
            protagonist_traits = story.lorebook.protagonist_state.get('traits', []) if story.lorebook else []
            available_choices = AIService.generate_choices(
                story=story,
                narrative_passage=narrative_text,
                protagonist_traits=protagonist_traits
            )
        
        # Créer le nouveau turn
        new_turn = StoryTurn(
            story_id=story.id,
            turn_number=story.turn_count + 1,
            narrative_text=narrative_text,
            player_action=data.action,
            action_type='choice' if data.selected_choice_index is not None else 'free_text',
            is_technical=data.is_technical,
            available_choices=available_choices,
            selected_choice_index=data.selected_choice_index,
            is_published=False
        )
        
        db.session.add(new_turn)
        story.turn_count += 1
        db.session.commit()
        
        # Mettre à jour le LoreBook si ce n'est pas un message technique
        if not data.is_technical and LoreBookService.should_update_lorebook(narrative_text, data.action):
            LoreBookService.update_lorebook(
                lorebook=story.lorebook,
                narrative_text=narrative_text,
                player_action=data.action,
                turn_number=story.turn_count
            )
        
        return jsonify({
            'message': 'Action traitée',
            'turn': new_turn.to_dict(),
            'story': story.to_dict()
        }), 200
        
    except ValidationError as e:
        return jsonify({
            'error': 'validation_error',
            'message': 'Données invalides',
            'details': e.errors()
        }), 400
        
    except Exception as e:
        db.session.rollback()
        return jsonify({
            'error': 'server_error',
            'message': f'Une erreur est survenue : {str(e)}'
        }), 500


@bp.route('/story/<story_id>/turns', methods=['GET'])
@jwt_required()
def get_story_turns(story_id: str) -> tuple:
    """Récupère l'historique complet des tours d'une histoire.
    
    Headers:
        Authorization: Bearer <access_token>
    
    Args:
        story_id: ID de l'histoire.
    
    Returns:
        Tuple (response, status_code).
    """
    try:
        user_id = get_jwt_identity()
        
        story = StoryService.get_story_by_id(story_id)
        
        if not story:
            return jsonify({
                'error': 'story_not_found',
                'message': 'Histoire non trouvée'
            }), 404
        
        if story.user_id != user_id:
            return jsonify({
                'error': 'forbidden',
                'message': 'Vous n\'avez pas accès à cette histoire'
            }), 403
        
        turns = StoryTurn.query.filter_by(story_id=story_id)\
            .order_by(StoryTurn.turn_number)\
            .all()
        
        return jsonify({
            'turns': [turn.to_dict() for turn in turns],
            'count': len(turns)
        }), 200
        
    except Exception as e:
        return jsonify({
            'error': 'server_error',
            'message': 'Une erreur est survenue'
        }), 500

