"""
Rating Service - Business logic for mandatory bidirectional rating enforcement
"""
from datetime import datetime
from typing import Dict, Any, Optional, Tuple
from flask import current_app
import logging

logger = logging.getLogger(__name__)


class RatingService:
    """Centralized rating business logic with mandatory enforcement"""
    
    def __init__(self):
        self.db = None  # Will be injected when used
    
    def _get_db(self):
        """Get database session"""
        if not self.db:
            from app.extensions import db
            self.db = db
        return self.db
    
    def _get_models(self):
        """Get required models"""
        from app.models.contract import Contract
        from app.models.rating import Rating
        from app.models.user import User
        return Contract, Rating, User
    
    def enforce_mandatory_rating(self, contract_id: int) -> Dict[str, Any]:
        """
        CRITICAL: Check if both parties have rated before allowing contract completion
        
        Returns:
            Dict with 'can_complete', 'message', 'pending_ratings'
        """
        try:
            Contract, Rating, User = self._get_models()
            contract = Contract.query.get(contract_id)
            
            if not contract:
                return {
                    'can_complete': False,
                    'message': 'Contract not found',
                    'pending_ratings': []
                }
            
            # Check if both parties have rated
            mutual_rating_complete = self.check_mutual_rating_complete(contract_id)
            
            if mutual_rating_complete['is_complete']:
                return {
                    'can_complete': True,
                    'message': 'All ratings complete - contract can be closed',
                    'pending_ratings': []
                }
            
            # Get pending ratings details
            pending_ratings = []
            if not contract.contractor_rated:
                contractor = User.query.get(contract.contractor_id)
                pending_ratings.append({
                    'role': 'contractor',
                    'user_id': contract.contractor_id,
                    'user_name': f"{contractor.first_name} {contractor.last_name}",
                    'needs_to_rate': 'worker'
                })
            
            if not contract.worker_rated:
                worker = User.query.get(contract.worker_id)
                pending_ratings.append({
                    'role': 'worker',
                    'user_id': contract.worker_id,
                    'user_name': f"{worker.first_name} {worker.last_name}",
                    'needs_to_rate': 'contractor'
                })
            
            return {
                'can_complete': False,
                'message': f'Contract cannot be completed. {len(pending_ratings)} rating(s) still pending.',
                'pending_ratings': pending_ratings
            }
            
        except Exception as e:
            logger.error(f"Error enforcing mandatory rating for contract {contract_id}: {e}")
            return {
                'can_complete': False,
                'message': 'Error checking rating status',
                'pending_ratings': []
            }
    
    def check_mutual_rating_complete(self, contract_id: int) -> Dict[str, Any]:
        """Check if both contractor and worker have rated each other"""
        try:
            Contract, Rating, User = self._get_models()
            contract = Contract.query.get(contract_id)
            
            if not contract:
                return {
                    'is_complete': False,
                    'error': 'Contract not found'
                }
            
            # Use the existing method from contract model
            is_complete = contract.is_mutual_rating_complete()
            
            return {
                'is_complete': is_complete,
                'contractor_rated': contract.contractor_rated,
                'worker_rated': contract.worker_rated,
                'mutual_rating_date': contract.mutual_rating_completed_date.isoformat() if contract.mutual_rating_completed_date else None
            }
            
        except Exception as e:
            logger.error(f"Error checking mutual rating for contract {contract_id}: {e}")
            return {
                'is_complete': False,
                'error': str(e)
            }
    
    def validate_rating_eligibility(self, contract_id: int, user_id: int) -> Dict[str, Any]:
        """Verify user can rate this contract"""
        try:
            Contract, Rating, User = self._get_models()
            contract = Contract.query.get(contract_id)
            
            if not contract:
                return {
                    'can_rate': False,
                    'error': 'Contract not found'
                }
            
            # Check if user is party to this contract
            if user_id not in [contract.contractor_id, contract.worker_id]:
                return {
                    'can_rate': False,
                    'error': 'User is not a party to this contract'
                }
            
            # Check if contract is in ratable status
            if contract.status not in ['pending_rating', 'completed']:
                return {
                    'can_rate': False,
                    'error': f'Contract status "{contract.status}" does not allow rating'
                }
            
            # Check if user has already rated
            if user_id == contract.contractor_id and contract.contractor_rated:
                return {
                    'can_rate': False,
                    'error': 'Contractor has already rated this contract'
                }
            
            if user_id == contract.worker_id and contract.worker_rated:
                return {
                    'can_rate': False,
                    'error': 'Worker has already rated this contract'
                }
            
            # Determine who the user would be rating
            rated_user_id = contract.worker_id if user_id == contract.contractor_id else contract.contractor_id
            rated_user = User.query.get(rated_user_id)
            
            return {
                'can_rate': True,
                'contract': {
                    'id': contract.id,
                    'status': contract.status
                },
                'rating_target': {
                    'user_id': rated_user_id,
                    'name': f"{rated_user.first_name} {rated_user.last_name}",
                    'role': 'worker' if rated_user_id == contract.worker_id else 'contractor'
                }
            }
            
        except Exception as e:
            logger.error(f"Error validating rating eligibility for contract {contract_id}, user {user_id}: {e}")
            return {
                'can_rate': False,
                'error': str(e)
            }
    
    def create_rating(self, contract_id: int, rater_id: int, rating_data: Dict[str, Any]) -> Dict[str, Any]:
        """Create rating with validation and update contract status"""
        try:
            Contract, Rating, User = self._get_models()
            db = self._get_db()
            
            # Validate eligibility first
            eligibility = self.validate_rating_eligibility(contract_id, rater_id)
            if not eligibility['can_rate']:
                return {
                    'success': False,
                    'error': eligibility['error']
                }
            
            contract = Contract.query.get(contract_id)
            rated_user_id = eligibility['rating_target']['user_id']
            
            # Validate rating scores (1-5 scale)
            overall_score = rating_data.get('overall_score')
            if not overall_score or not (1 <= overall_score <= 5):
                return {
                    'success': False,
                    'error': 'Overall score must be between 1 and 5'
                }
            
            # Create the rating
            rating = Rating(
                contract_id=contract_id,
                rater_id=rater_id,
                rated_id=rated_user_id,
                overall_score=overall_score,
                quality_score=rating_data.get('quality_score'),
                communication_score=rating_data.get('communication_score'),
                reliability_score=rating_data.get('reliability_score'),
                professionalism_score=rating_data.get('professionalism_score'),
                review_text=rating_data.get('review_text', ''),
                is_public=rating_data.get('is_public', True)
            )
            
            db.session.add(rating)
            
            # Update contract rating status
            if rater_id == contract.contractor_id:
                contract.contractor_rated = True
            else:
                contract.worker_rated = True
            
            # Check if both parties have now rated
            if contract.contractor_rated and contract.worker_rated:
                contract.mark_as_completed()  # Use method to properly set status, completion_status, and increment jobs_completed
                contract.mutual_rating_completed_date = datetime.utcnow()
                
                # TRIGGER PAYMENT DISTRIBUTION
                # When both parties have rated, automatically release payment to worker
                payment = contract.payments.filter_by(status='pending_release').first()
                if not payment:
                    # Also check for held_escrow status (payment might not have gone through mark complete flow)
                    payment = contract.payments.filter_by(status='held_escrow').first()
                
                if payment:
                    try:
                        from ..services.stripe_service import stripe_service
                        from flask import current_app
                        
                        worker = contract.worker
                        
                        print(f"💰 Auto-releasing payment after mutual rating completion")
                        print(f"   Contract: {contract.id}, Payment: {payment.payment_reference}")
                        
                        # Step 1: Capture payment from escrow
                        if payment.stripe_payment_intent_id:
                            print(f"📥 Capturing payment from escrow...")
                            captured_payment = stripe_service.capture_payment(
                                payment.stripe_payment_intent_id
                            )
                            payment.stripe_status = captured_payment.status
                            print(f"✅ Payment captured: {captured_payment.status}")
                        
                        # Step 2: Transfer to worker
                        transfer_success = False
                        
                        # Create real Stripe transfer (works in both test and production mode)
                        if worker.stripe_account_id:
                            print(f"💸 Transferring ${payment.net_to_worker} to worker...")
                            try:
                                transfer = stripe_service.transfer_to_worker(
                                    amount_aud=float(payment.net_to_worker),
                                    destination_account_id=worker.stripe_account_id,
                                    metadata={
                                        'contract_id': contract.id,
                                        'payment_reference': payment.payment_reference,
                                        'worker_email': worker.email
                                    }
                                )
                                payment.stripe_transfer_id = transfer.id
                                payment.stripe_transfer_status = 'succeeded'  # Transfers are immediate
                                transfer_success = True
                                print(f"✅ Transfer created: {transfer.id}")
                            except Exception as transfer_error:
                                print(f"⚠️ Transfer failed: {transfer_error}")
                                # If platform doesn't have enough balance in test mode, log but continue
                                if 'insufficient funds' in str(transfer_error).lower():
                                    print(f"💡 Note: Add funds to platform balance using test card 4000000000000077")
                        else:
                            print(f"⚠️ Worker has no Stripe account - payment held")
                        
                        # Step 3: Update payment status - ALWAYS update if transfer succeeded
                        if transfer_success or payment.stripe_transfer_id:
                            payment.status = 'transferred'
                            payment.date_released = datetime.utcnow()
                            print(f"🎉 Payment distributed: ${payment.net_to_worker} to worker")
                            
                            # Auto-generate invoice for GST-registered workers
                            try:
                                invoice = payment.auto_generate_invoice()
                                if invoice:
                                    print(f"📄 Auto-generated invoice: {invoice.invoice_number}")
                                    db.session.flush()
                            except Exception as invoice_error:
                                print(f"⚠️ Invoice generation failed: {invoice_error}")
                                logger.error(f"Invoice generation error: {invoice_error}")
                        
                    except Exception as payment_error:
                        logger.error(f"Payment distribution failed: {payment_error}")
                        # Don't fail the rating if payment fails - can be retried
                        # BUT if transfer was created, still update status
                        if payment.stripe_transfer_id and payment.status == 'pending_release':
                            payment.status = 'transferred'
                            payment.date_released = datetime.utcnow()
                            print(f"⚠️ Exception occurred but transfer was created - marking as transferred")
                
                status_message = 'Rating submitted! Contract is now complete.'
            else:
                contract.status = 'pending_rating'  # Ensure contract stays in rating state
                status_message = 'Rating submitted! Waiting for the other party to rate.'
            
            db.session.commit()
            
            # Safety check: Fix any stuck payments that have transfer_id but wrong status
            payment = contract.payments.filter_by(status='pending_release').first()
            if not payment:
                payment = contract.payments.filter_by(status='held_escrow').first()
            
            if payment and payment.stripe_transfer_id and payment.status == 'pending_release':
                print(f"⚠️ SAFETY CHECK: Payment {payment.id} has transfer but wrong status - fixing...")
                payment.status = 'transferred'
                if not payment.date_released:
                    payment.date_released = datetime.utcnow()
                
                # Auto-generate invoice for GST-registered workers
                try:
                    invoice = payment.auto_generate_invoice()
                    if invoice:
                        print(f"📄 Auto-generated invoice (safety check): {invoice.invoice_number}")
                except Exception as invoice_error:
                    print(f"⚠️ Invoice generation failed: {invoice_error}")
                
                db.session.commit()
                print(f"✅ Fixed stuck payment status")

            # Update rated user's average rating
            rated_user = User.query.get(rated_user_id)
            if rated_user:
                user_ratings = Rating.query.filter_by(rated_id=rated_user_id).all()
                if user_ratings:
                    avg_score = sum(r.overall_score for r in user_ratings) / len(user_ratings)
                    rated_user.average_rating = round(avg_score, 2)
                    db.session.commit()
                    db.session.refresh(rated_user)
            
            # Send notifications
            try:
                self._send_rating_notifications(contract_id, rater_id, rated_user_id)
            except Exception as notification_error:
                logger.warning(f"Rating notification failed: {notification_error}")
            
            return {
                'success': True,
                'message': status_message,
                'rating_id': rating.id,
                'contract_status': contract.status,
                'mutual_complete': contract.contractor_rated and contract.worker_rated
            }
            
        except Exception as e:
            if self.db:
                self.db.session.rollback()
            logger.error(f"Error creating rating for contract {contract_id}: {e}")
            return {
                'success': False,
                'error': f'Failed to create rating: {str(e)}'
            }
    
    def get_contract_ratings(self, contract_id: int) -> Dict[str, Any]:
        """Get all ratings for a contract"""
        try:
            Contract, Rating, User = self._get_models()
            
            contract = Contract.query.get(contract_id)
            if not contract:
                return {
                    'success': False,
                    'error': 'Contract not found'
                }
            
            ratings = Rating.query.filter_by(contract_id=contract_id).all()
            
            ratings_data = []
            for rating in ratings:
                rater = User.query.get(rating.rater_id)
                rated = User.query.get(rating.rated_id)
                
                ratings_data.append({
                    'id': rating.id,
                    'overall_score': rating.overall_score,
                    'quality_score': rating.quality_score,
                    'communication_score': rating.communication_score,
                    'reliability_score': rating.reliability_score,
                    'professionalism_score': rating.professionalism_score,
                    'review_text': rating.review_text,
                    'rating_date': rating.rating_date.isoformat(),
                    'is_public': rating.is_public,
                    'rater': {
                        'id': rater.id,
                        'name': f"{rater.first_name} {rater.last_name}",
                        'role': 'contractor' if rating.rater_id == contract.contractor_id else 'worker'
                    },
                    'rated': {
                        'id': rated.id,
                        'name': f"{rated.first_name} {rated.last_name}",
                        'role': 'contractor' if rating.rated_id == contract.contractor_id else 'worker'
                    }
                })
            
            return {
                'success': True,
                'ratings': ratings_data,
                'total_ratings': len(ratings_data),
                'contractor_rated': contract.contractor_rated,
                'worker_rated': contract.worker_rated,
                'mutual_complete': contract.contractor_rated and contract.worker_rated
            }
            
        except Exception as e:
            logger.error(f"Error getting ratings for contract {contract_id}: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def get_rating_status_for_user(self, contract_id: int, user_id: int) -> Dict[str, Any]:
        """Get rating status specific to a user"""
        try:
            Contract, Rating, User = self._get_models()
            contract = Contract.query.get(contract_id)
            
            if not contract:
                return {
                    'status': 'error',
                    'error': 'Contract not found'
                }
            
            # Use existing method from contract model
            status = contract.get_rating_status_for_user(user_id)
            
            # Get additional context
            if user_id == contract.contractor_id:
                user_role = 'contractor'
                target_role = 'worker'
                user_has_rated = contract.contractor_rated
            elif user_id == contract.worker_id:
                user_role = 'worker'
                target_role = 'contractor'
                user_has_rated = contract.worker_rated
            else:
                return {
                    'status': 'not_authorized',
                    'error': 'User is not a party to this contract'
                }
            
            return {
                'status': status,
                'user_role': user_role,
                'target_role': target_role,
                'user_has_rated': user_has_rated,
                'can_rate': not user_has_rated and contract.status in ['pending_rating', 'completed'],
                'contract_status': contract.status
            }
            
        except Exception as e:
            logger.error(f"Error getting rating status for contract {contract_id}, user {user_id}: {e}")
            return {
                'status': 'error',
                'error': str(e)
            }
    
    def transition_contract_to_rating_stage(self, contract_id: int) -> Dict[str, Any]:
        """Transition contract from pending_review to pending_rating"""
        try:
            Contract, Rating, User = self._get_models()
            db = self._get_db()
            
            contract = Contract.query.get(contract_id)
            if not contract:
                return {
                    'success': False,
                    'error': 'Contract not found'
                }
            
            # Only transition from pending_review to pending_rating
            if contract.status != 'pending_review':
                return {
                    'success': False,
                    'error': f'Contract must be in pending_review status, currently: {contract.status}'
                }
            
            # Update status to pending_rating
            contract.status = 'pending_rating'
            db.session.commit()
            
            # Send notifications to both parties
            try:
                self._send_rating_required_notifications(contract_id)
            except Exception as notification_error:
                logger.warning(f"Rating notification failed: {notification_error}")
            
            return {
                'success': True,
                'message': 'Contract transitioned to rating stage',
                'new_status': 'pending_rating'
            }
            
        except Exception as e:
            if self.db:
                self.db.session.rollback()
            logger.error(f"Error transitioning contract {contract_id} to rating stage: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def _send_rating_required_notifications(self, contract_id: int) -> None:
        """Send notifications to both parties that ratings are required"""
        try:
            from app.services.notification_service import notification_service
            from app.models.notification import NotificationType
            from flask import url_for
            
            Contract, Rating, User = self._get_models()
            contract = Contract.query.get(contract_id)
            
            if not contract:
                return
            
            # Send to contractor if not rated
            if not contract.contractor_rated:
                notification_service.send_notification(
                    user_id=contract.contractor_id,
                    notification_type=NotificationType.CONTRACT_RATING_REQUIRED,
                    title="Rating Required",
                    content=f"Please rate the worker for contract #{contract.id}. The contract cannot be completed until both parties provide ratings.",
                    action_url=url_for('rate_contract', contract_id=contract.id, _external=False)
                )
            
            # Send to worker if not rated
            if not contract.worker_rated:
                notification_service.send_notification(
                    user_id=contract.worker_id,
                    notification_type=NotificationType.CONTRACT_RATING_REQUIRED,
                    title="Rating Required",
                    content=f"Please rate the contractor for contract #{contract.id}. The contract cannot be completed until both parties provide ratings.",
                    action_url=url_for('rate_contract', contract_id=contract.id, _external=False)
                )
            
        except Exception as e:
            logger.error(f"Error sending rating required notifications for contract {contract_id}: {e}")
            # Don't re-raise as this is non-critical
    
    def _send_rating_notifications(self, contract_id: int, rater_id: int, rated_user_id: int) -> None:
        """Send notifications after rating is submitted"""
        try:
            from app.services.notification_service import notification_service
            from app.models.notification import NotificationType
            from flask import url_for
            
            Contract, Rating, User = self._get_models()
            contract = Contract.query.get(contract_id)
            rater = User.query.get(rater_id)
            
            if not contract or not rater:
                return
            
            # Notify the rated user that they received a rating
            notification_service.send_notification(
                user_id=rated_user_id,
                notification_type=NotificationType.CONTRACT_RATING_RECEIVED,
                title="Rating Received",
                content=f"{rater.first_name} {rater.last_name} has rated you for contract #{contract.id}",
                action_url=url_for('contracts_review', contract_id=contract.id, _external=False)
            )
            
            # If contract is now complete, notify both parties
            if contract.contractor_rated and contract.worker_rated:
                for user_id in [contract.contractor_id, contract.worker_id]:
                    notification_service.send_notification(
                        user_id=user_id,
                        notification_type=NotificationType.CONTRACT_COMPLETED,
                        title="Contract Completed",
                        content=f"Contract #{contract.id} is now fully completed with mutual ratings!",
                        action_url=url_for('contracts_review', contract_id=contract.id, _external=False)
                    )
            
        except Exception as e:
            logger.error(f"Error sending rating notifications for contract {contract_id}: {e}")
            # Don't re-raise as this is non-critical
    
    def get_pending_ratings_for_user(self, user_id: int) -> Dict[str, Any]:
        """Get all contracts requiring rating from this user"""
        try:
            Contract, Rating, User = self._get_models()
            
            # Find contracts where user needs to provide rating
            contracts_needing_rating = []
            
            # Check as contractor
            contractor_contracts = Contract.query.filter_by(
                contractor_id=user_id,
                status='pending_rating'
            ).filter_by(contractor_rated=False).all()
            
            for contract in contractor_contracts:
                worker = User.query.get(contract.worker_id)
                contracts_needing_rating.append({
                    'contract_id': contract.id,
                    'user_role': 'contractor',
                    'target_role': 'worker',
                    'target_name': f"{worker.first_name} {worker.last_name}",
                    'job_title': contract.job.title if contract.job else 'Contract Work',
                    'agreed_rate': float(contract.agreed_rate),
                    'status': contract.status
                })
            
            # Check as worker
            worker_contracts = Contract.query.filter_by(
                worker_id=user_id,
                status='pending_rating'
            ).filter_by(worker_rated=False).all()
            
            for contract in worker_contracts:
                contractor = User.query.get(contract.contractor_id)
                contracts_needing_rating.append({
                    'contract_id': contract.id,
                    'user_role': 'worker',
                    'target_role': 'contractor',
                    'target_name': f"{contractor.first_name} {contractor.last_name}",
                    'job_title': contract.job.title if contract.job else 'Contract Work',
                    'agreed_rate': float(contract.agreed_rate),
                    'status': contract.status
                })
            
            return {
                'success': True,
                'pending_ratings': contracts_needing_rating,
                'total_pending': len(contracts_needing_rating)
            }
            
        except Exception as e:
            logger.error(f"Error getting pending ratings for user {user_id}: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def validate_contract_completion_request(self, contract_id: int, requester_id: int) -> Dict[str, Any]:
        """
        CRITICAL: Validate that a contract completion request can proceed
        This is the main enforcement point for mandatory ratings
        """
        try:
            Contract, Rating, User = self._get_models()
            contract = Contract.query.get(contract_id)
            
            if not contract:
                return {
                    'can_complete': False,
                    'error': 'Contract not found'
                }
            
            # Check if requester is authorized
            if requester_id not in [contract.contractor_id, contract.worker_id]:
                return {
                    'can_complete': False,
                    'error': 'User is not authorized for this contract'
                }
            
            # Check current contract status
            if contract.status not in ['pending_review', 'pending_rating']:
                return {
                    'can_complete': False,
                    'error': f'Contract status "{contract.status}" does not allow completion'
                }
            
            # MANDATORY RATING ENFORCEMENT
            rating_check = self.enforce_mandatory_rating(contract_id)
            
            if not rating_check['can_complete']:
                return {
                    'can_complete': False,
                    'error': rating_check['message'],
                    'pending_ratings': rating_check['pending_ratings'],
                    'requires_rating': True
                }
            
            return {
                'can_complete': True,
                'message': 'Contract can be completed - all requirements met'
            }
            
        except Exception as e:
            logger.error(f"Error validating contract completion for {contract_id}: {e}")
            return {
                'can_complete': False,
                'error': str(e)
            }


# Create singleton instance
rating_service = RatingService()
