﻿"""
Rating routes for contract completion and user feedback.
Handles rating submission, validation, and user rating updates.
"""

from flask import Blueprint, request, jsonify, current_app
from flask_login import login_required, current_user
from sqlalchemy import func
from datetime import datetime, timezone
from typing import Dict, Any, Tuple

from app.extensions import db, limiter
from app.models.contract import Contract
from app.models.rating import Rating
from app.models.user import User
from app.utils.validators import validate_rating_data
from app.utils.gamification import award_points
from app.config.achievements import check_user_achievements
from app.utils.notification_helpers import notify_rating_received

rating_bp = Blueprint('rating', __name__, url_prefix='/api/contracts')


@rating_bp.route('/<int:contract_id>/rate', methods=['POST'])
@login_required
def submit_rating(contract_id: int) -> Tuple[Dict[str, Any], int]:
    """
    Submit a rating for a completed contract.

    Args:
        contract_id: The ID of the contract to rate

    Returns:
        JSON response with success status and updated user average

    Expected JSON payload:
    {
        "quality": 5,           # 1-5 rating for work quality
        "timeliness": 4,        # 1-5 rating for timeliness
        "communication": 5,     # 1-5 rating for communication
        "professionalism": 4,   # 1-5 rating for professionalism
        "comment": "Great work!" # Optional comment
    }
    """
    try:
        # Get and validate request data
        data = request.get_json()
        if not data:
            return {"error": "No data provided"}, 400

        # Validate contract exists and is accessible
        contract = Contract.query.get(contract_id)
        if not contract:
            return {"error": "Contract not found"}, 404

        # Check contract is completed
        if contract.status != 'completed':
            return {"error": "Can only rate completed contracts"}, 400

        # Determine who is rating whom
        if current_user.id == contract.contractor_id:
            # Contractor rating worker
            rater_id = contract.contractor_id
            ratee_id = contract.worker_id
            rater_role = 'contractor'
        elif current_user.id == contract.worker_id:
            # Worker rating contractor
            rater_id = contract.worker_id
            ratee_id = contract.contractor_id
            rater_role = 'worker'
        else:
            return {"error": "Not authorized to rate this contract"}, 403

        # Check for duplicate rating
        existing_rating = Rating.query.filter_by(
            contract_id=contract_id,
            rater_id=rater_id
        ).first()

        if existing_rating:
            return {"error": "You have already rated this contract"}, 409

        # Validate rating data
        validation_result = validate_rating_data(data)
        if not validation_result['valid']:
            return {"error": validation_result['message']}, 400

        # Calculate overall rating as average of individual ratings
        individual_ratings = [
            data.get('quality', 0),
            data.get('timeliness', 0),
            data.get('communication', 0),
            data.get('professionalism', 0)
        ]

        # Filter out zero ratings (not provided)
        valid_ratings = [r for r in individual_ratings if r > 0]
        if not valid_ratings:
            return {"error": "At least one rating category is required"}, 400

        overall_rating = sum(valid_ratings) / len(valid_ratings)

        # Create new rating record
        new_rating = Rating(
            contract_id=contract_id,
            rater_id=rater_id,
            rated_id=ratee_id,
            overall_score=round(overall_rating, 2),
            quality_score=data.get('quality'),
            reliability_score=data.get('timeliness'),  # timeliness maps to reliability
            communication_score=data.get('communication'),
            professionalism_score=data.get('professionalism'),
            review_text=data.get('comment', '').strip()[:500],  # Limit comment length
        )

        db.session.add(new_rating)

        # Update ratee's average rating
        ratee = User.query.get(ratee_id)
        if ratee:
            # Calculate new average rating for the user
            user_ratings = Rating.query.filter_by(rated_id=ratee_id).all()
            if user_ratings:
                total_rating = sum(r.overall_score for r in user_ratings) + overall_rating
                total_count = len(user_ratings) + 1
                new_average = round(total_rating / total_count, 2)

                # Update user's cached average rating and review count
                ratee.average_rating = new_average
                ratee.total_reviews = total_count

        db.session.commit()

        # NOTIFICATIONS: Send notification to ratee about the new rating
        try:
            rater = User.query.get(rater_id)
            rater_name = f"{rater.first_name} {rater.last_name}" if rater else "Someone"

            # Get job/contract title for notification
            job_title = getattr(contract, 'title', f"Contract #{contract_id}")

            notify_rating_received(
                user_id=ratee_id,
                rating=overall_rating,
                reviewer_name=rater_name,
                job_title=job_title,
                review_id=new_rating.id
            )

            current_app.logger.info(f"Rating notification sent to user {ratee_id}")

        except Exception as notification_error:
            # Don't fail the rating if notification fails
            current_app.logger.error(f"Notification error: {notification_error}")

        # GAMIFICATION: Award points and check achievements
        try:
            # Award points to rater for giving a rating
            award_points(rater_id, 'rating_given', related_contract_id=contract_id)

            # Award bonus points to ratee if they got a 5-star rating
            if overall_rating >= 5.0:
                award_points(ratee_id, 'five_star_received', related_contract_id=contract_id)

            # Check if ratee unlocked any achievements
            new_achievements = check_user_achievements(ratee_id)

            # Also check rater achievements (they might have leveled up from points)
            rater_achievements = check_user_achievements(rater_id)

            current_app.logger.info(
                f"Gamification: Rater {rater_id} earned 10 points. "
                f"Ratee {ratee_id} earned {50 if overall_rating >= 5.0 else 0} bonus points. "
                f"New achievements: Ratee={new_achievements}, Rater={rater_achievements}"
            )

        except Exception as gamification_error:
            # Don't fail the rating if gamification fails
            current_app.logger.error(f"Gamification error: {gamification_error}")

        current_app.logger.info(
            f"Rating submitted: Contract {contract_id}, "
            f"Rater {rater_id} rated {ratee_id} with {overall_rating}"
        )

        return {
            "success": True,
            "message": "Rating submitted successfully",
            "rating": {
                "overall_rating": overall_rating,
                "quality": data.get('quality'),
                "timeliness": data.get('timeliness'),
                "communication": data.get('communication'),
                "professionalism": data.get('professionalism'),
                "comment": new_rating.review_text
            },
            "ratee_new_average": float(ratee.average_rating) if ratee else None,
            "ratee_total_reviews": ratee.total_reviews if ratee else None
        }, 201

    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Error submitting rating: {str(e)}")
        return {"error": "Internal server error"}, 500


@rating_bp.route('/<int:contract_id>/rating', methods=['GET'])
@login_required
def get_contract_rating(contract_id: int) -> Tuple[Dict[str, Any], int]:
    """
    Get rating information for a specific contract.

    Args:
        contract_id: The ID of the contract

    Returns:
        JSON response with rating data or null if no rating exists
    """
    try:
        contract = Contract.query.get(contract_id)
        if not contract:
            return {"error": "Contract not found"}, 404

        # Check user has access to this contract
        if current_user.id not in [contract.contractor_id, contract.worker_id]:
            return {"error": "Not authorized to view this contract's rating"}, 403

        # Get rating by current user for this contract
        rating = Rating.query.filter_by(
            contract_id=contract_id,
            rater_id=current_user.id
        ).first()

        if not rating:
            return {"rating": None, "can_rate": contract.status == 'completed'}, 200

        return {
            "rating": {
                "overall_rating": float(rating.overall_score),
                "quality": rating.quality_score,
                "timeliness": rating.reliability_score,  # timeliness maps to reliability
                "communication": rating.communication_score,
                "professionalism": rating.professionalism_score,
                "comment": rating.review_text,
                "created_at": rating.rating_date.isoformat()
            },
            "can_rate": False  # Already rated
        }, 200

    except Exception as e:
        current_app.logger.error(f"Error getting contract rating: {str(e)}")
        return {"error": "Internal server error"}, 500


@rating_bp.route('/users/<int:user_id>/ratings', methods=['GET'])
def get_user_ratings(user_id: int) -> Tuple[Dict[str, Any], int]:
    """
    Get all ratings for a specific user with pagination.

    Args:
        user_id: The ID of the user to get ratings for

    Query Parameters:
        page: Page number (default: 1)
        per_page: Items per page (default: 10, max: 50)

    Returns:
        JSON response with user ratings and pagination info
    """
    try:
        # Validate user exists
        user = User.query.get(user_id)
        if not user:
            return {"error": "User not found"}, 404

        # Get pagination parameters
        page = request.args.get('page', 1, type=int)
        per_page = min(request.args.get('per_page', 10, type=int), 50)

        # Get paginated ratings for this user
        ratings_query = Rating.query.filter_by(rated_id=user_id).order_by(Rating.rating_date.desc())
        ratings_paginated = ratings_query.paginate(
            page=page,
            per_page=per_page,
            error_out=False
        )

        # Format ratings data
        ratings_data = []
        for rating in ratings_paginated.items:
            # Get rater info
            rater = User.query.get(rating.rater_id)

            rating_data = {
                "id": rating.id,
                "overall_rating": float(rating.overall_score),
                "quality": rating.quality_score,
                "timeliness": rating.reliability_score,  # timeliness maps to reliability
                "communication": rating.communication_score,
                "professionalism": rating.professionalism_score,
                "comment": rating.review_text,
                "created_at": rating.rating_date.isoformat(),
                "rater": {
                    "id": rater.id if rater else None,
                    "name": f"{rater.first_name} {rater.last_name}" if rater else "Unknown",
                    "role": rater.role if rater else None
                },
                "contract_id": rating.contract_id
            }
            ratings_data.append(rating_data)

        # Calculate user's current average rating
        all_ratings = Rating.query.filter_by(rated_id=user_id).all()
        if all_ratings:
            total_rating = sum(r.overall_score for r in all_ratings)
            average_rating = round(total_rating / len(all_ratings), 2)
        else:
            average_rating = 0.0

        # Calculate rating distribution
        rating_distribution = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
        for rating in all_ratings:
            rounded_rating = round(rating.overall_score)
            if rounded_rating in rating_distribution:
                rating_distribution[rounded_rating] += 1

        return {
            "user": {
                "id": user.id,
                "name": f"{user.first_name} {user.last_name}",
                "role": user.role,
                "average_rating": average_rating,
                "total_reviews": len(all_ratings)
            },
            "ratings": ratings_data,
            "rating_distribution": rating_distribution,
            "pagination": {
                "page": page,
                "per_page": per_page,
                "total": ratings_paginated.total,
                "pages": ratings_paginated.pages,
                "has_next": ratings_paginated.has_next,
                "has_prev": ratings_paginated.has_prev,
                "next_num": ratings_paginated.next_num,
                "prev_num": ratings_paginated.prev_num
            }
        }, 200

    except Exception as e:
        current_app.logger.error(f"Error getting user ratings: {str(e)}")
        return {"error": "Internal server error"}, 500




@rating_bp.route('/<int:contract_id>/status', methods=['GET'])
@login_required
@limiter.exempt
def get_contract_status(contract_id: int) -> Tuple[Dict[str, Any], int]:
    """Quick status check for contract"""
    from app.models.contract import Contract

    contract = Contract.query.get_or_404(contract_id)

    if current_user.id not in [contract.contractor_id, contract.worker_id]:
        return {'error': 'Unauthorized'}, 403

    return {
        'status': contract.status,
        'contractor_rated': contract.contractor_rated,
        'worker_rated': contract.worker_rated
    }, 200@rating_bp.errorhandler(404)
def not_found(error):
    """Handle 404 errors for rating routes."""
    return {"error": "Resource not found"}, 404


@rating_bp.errorhandler(500)
def internal_error(error):
    """Handle 500 errors for rating routes."""
    db.session.rollback()
    return {"error": "Internal server error"}, 500
