"""Test rating submission and calculation"""
import pytest
from datetime import datetime, date, timedelta
from app import create_app
from app.extensions import db
from app.models import User, Contract, Job, Application
from app.models.safety import Review
from app.services.rating_service import RatingService


@pytest.fixture
def app():
    """Create and configure test app"""
    app = create_app()
    app.config.update({
        'TESTING': True,
        'SQLALCHEMY_DATABASE_URI': 'sqlite:///:memory:',
        'WTF_CSRF_ENABLED': False,
        'SECRET_KEY': 'test-secret-key'
    })
    
    with app.app_context():
        db.create_all()
        yield app
        db.session.remove()
        db.drop_all()


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


@pytest.fixture
def sample_users(app):
    """Create sample users for testing"""
    contractor = User(
        email='contractor@test.com',
        first_name='Test',
        last_name='Contractor',
        role='contractor',
        phone_number='0400000001',
        location='Sydney, NSW',
        abn_number='12345678901',
        is_active=True,
        privacy_consent=True,
        terms_accepted=True,
        terms_accepted_date=datetime.utcnow()
    )
    contractor.set_password('password123')
    
    worker = User(
        email='worker@test.com',
        first_name='Test',
        last_name='Worker',
        role='worker',
        phone_number='0400000002',
        location='Melbourne, VIC',
        abn_number='12345678902',
        is_active=True,
        privacy_consent=True,
        terms_accepted=True,
        terms_accepted_date=datetime.utcnow()
    )
    worker.set_password('password123')
    
    db.session.add_all([contractor, worker])
    db.session.commit()
    
    return {
        'contractor': contractor,
        'worker': worker
    }


@pytest.fixture
def sample_contract(app, sample_users):
    """Create sample contract for testing"""
    # Create a category first
    from app.models.category import Category
    
    category = Category(
        name='General Construction',
        description='General construction work',
        whs_risk_level='medium',
        white_card_required=True
    )
    db.session.add(category)
    db.session.flush()
    
    # Create a job first
    job = Job(
        title='Test Construction Job',
        description='Test job for rating',
        contractor_id=sample_users['contractor'].id,
        category_id=category.id,
        location='Sydney, NSW',
        budget_min=1000.00,
        budget_max=2000.00,
        status='assigned'
    )
    db.session.add(job)
    db.session.flush()
    
    # Create contract
    contract = Contract(
        job_id=job.id,
        contractor_id=sample_users['contractor'].id,
        worker_id=sample_users['worker'].id,
        agreed_rate=1500.00,
        rate_type='total',
        start_date=date.today() - timedelta(days=30),
        end_date=date.today() - timedelta(days=1),
        scope_of_work='Test construction work',
        status='completed'
    )
    db.session.add(contract)
    db.session.commit()
    
    return contract


def login_user(client, email, password):
    """Helper function to log in a user"""
    return client.post('/login', data={
        'email': email,
        'password': password
    }, follow_redirects=True)


class TestRatingSystem:
    """Test rating system functionality"""
    
    def test_submit_rating_success(self, client, sample_contract, sample_users):
        """Test successful rating submission"""
        # Login as contractor
        login_user(client, 'contractor@test.com', 'password123')
        
        # Submit rating for worker
        response = client.post(f'/contracts/{sample_contract.id}/rate', data={
            'overall_rating': '5',
            'quality_rating': '5',
            'communication_rating': '4',
            'safety_rating': '5',
            'comment': 'Excellent work!',
            'would_work_again': 'on'
        })
        
        # Should redirect after successful submission
        assert response.status_code == 302
        
        # Check rating was created
        review = Review.query.filter_by(
            contract_id=sample_contract.id,
            reviewer_id=sample_users['contractor'].id
        ).first()
        
        assert review is not None
        assert review.overall_rating == 5
        assert review.quality_rating == 5
        assert review.communication_rating == 4
        assert review.safety_rating == 5
        assert review.comment == 'Excellent work!'
        assert review.would_work_again is True
    
    def test_submit_rating_invalid_values(self, client, sample_contract, sample_users):
        """Test rating with invalid values"""
        # Login as contractor
        login_user(client, 'contractor@test.com', 'password123')
        
        # Try to submit rating with invalid values
        response = client.post(f'/contracts/{sample_contract.id}/rate', data={
            'overall_rating': '6',  # Invalid - should be 1-5
            'quality_rating': '0',  # Invalid - should be 1-5
            'communication_rating': '3',
            'safety_rating': '4',
            'comment': 'Test comment'
        })
        
        # Should show form again with error
        assert response.status_code == 200
        assert b'All ratings must be between 1 and 5' in response.data
    
    def test_duplicate_rating_blocked(self, client, sample_contract, sample_users):
        """Test that duplicate ratings are blocked"""
        # Login as contractor
        login_user(client, 'contractor@test.com', 'password123')
        
        # First rating
        client.post(f'/contracts/{sample_contract.id}/rate', data={
            'overall_rating': '5',
            'quality_rating': '5',
            'communication_rating': '4',
            'safety_rating': '5',
            'comment': 'First rating'
        })
        
        # Try to submit second rating
        response = client.post(f'/contracts/{sample_contract.id}/rate', data={
            'overall_rating': '3',
            'quality_rating': '3',
            'communication_rating': '3',
            'safety_rating': '3',
            'comment': 'Second rating'
        })
        
        # Should redirect with info message
        assert response.status_code == 302
        
        # Should only have one review
        review_count = Review.query.filter_by(
            contract_id=sample_contract.id,
            reviewer_id=sample_users['contractor'].id
        ).count()
        
        assert review_count == 1
    
    def test_only_contract_parties_can_rate(self, client, sample_contract, sample_users):
        """Test that only contract parties can submit ratings"""
        # Create third user who is not part of the contract
        other_user = User(
            email='other@test.com',
            first_name='Other',
            last_name='User',
            role='worker',
            phone_number='0400000003',
            location='Brisbane, QLD',
            abn_number='12345678903',
            is_active=True,
            privacy_consent=True,
            terms_accepted=True,
            terms_accepted_date=datetime.utcnow()
        )
        other_user.set_password('password123')
        db.session.add(other_user)
        db.session.commit()
        
        # Login as other user
        login_user(client, 'other@test.com', 'password123')
        
        # Try to access rating form
        response = client.get(f'/contracts/{sample_contract.id}/rate')
        
        # Should be denied access
        assert response.status_code == 302  # Redirect
    
    def test_rating_calculation_with_service(self, app, sample_users):
        """Test rating calculation using the rating service"""
        with app.app_context():
            service = RatingService()
            worker = sample_users['worker']
            
            # Create category first
            from app.models.category import Category
            
            category = Category(
                name='Service Test Category',
                description='Category for service testing',
                whs_risk_level='medium'
            )
            db.session.add(category)
            db.session.flush()
            
            # Create multiple contracts and ratings
            contracts = []
            for i in range(3):
                job = Job(
                    title=f'Test Job {i+1}',
                    description=f'Test job {i+1} for rating',
                    contractor_id=sample_users['contractor'].id,
                    category_id=category.id,
                    location='Sydney, NSW',
                    budget_min=1000.00,
                    budget_max=2000.00,
                    status='assigned'
                )
                db.session.add(job)
                db.session.flush()
                
                contract = Contract(
                    job_id=job.id,
                    contractor_id=sample_users['contractor'].id,
                    worker_id=worker.id,
                    agreed_rate=1500.00,
                    rate_type='total',
                    start_date=date.today() - timedelta(days=30),
                    end_date=date.today() - timedelta(days=1),
                    scope_of_work=f'Test work {i+1}',
                    status='completed'
                )
                db.session.add(contract)
                contracts.append(contract)
            
            db.session.commit()
            
            # Add ratings: 5, 4, 3 stars
            ratings = [5, 4, 3]
            for i, rating in enumerate(ratings):
                review = Review(
                    reviewer_id=sample_users['contractor'].id,
                    reviewee_id=worker.id,
                    job_id=contracts[i].job_id,
                    contract_id=contracts[i].id,
                    overall_rating=rating,
                    quality_rating=rating,
                    communication_rating=rating,
                    safety_rating=rating,
                    comment=f'Rating {rating} stars',
                    verified_completion=True
                )
                db.session.add(review)
            
            db.session.commit()
            
            # Calculate average rating
            avg_rating = service.calculate_average_rating(worker.id)
            
            # Should be (5 + 4 + 3) / 3 = 4.0
            assert abs(avg_rating - 4.0) < 0.01
    
    def test_contract_must_be_completed_for_rating(self, client, sample_users):
        """Test that only completed contracts can be rated"""
        # Create category first
        from app.models.category import Category
        
        category = Category(
            name='Active Test Category',
            description='Category for active contract testing'
        )
        db.session.add(category)
        db.session.flush()
        
        # Create active contract
        job = Job(
            title='Active Test Job',
            description='Active job for rating test',
            contractor_id=sample_users['contractor'].id,
            category_id=category.id,
            location='Sydney, NSW',
            budget_min=1000.00,
            budget_max=2000.00,
            status='assigned'
        )
        db.session.add(job)
        db.session.flush()
        
        active_contract = Contract(
            job_id=job.id,
            contractor_id=sample_users['contractor'].id,
            worker_id=sample_users['worker'].id,
            agreed_rate=1500.00,
            rate_type='total',
            start_date=date.today(),
            end_date=date.today() + timedelta(days=30),
            scope_of_work='Active work',
            status='active'  # Not completed
        )
        db.session.add(active_contract)
        db.session.commit()
        
        # Login as contractor
        login_user(client, 'contractor@test.com', 'password123')
        
        # Try to access rating form for active contract
        response = client.get(f'/contracts/{active_contract.id}/rate')
        
        # Should not be allowed
        assert response.status_code == 302  # Redirect with warning
    
    def test_rating_updates_user_average(self, app, sample_contract, sample_users):
        """Test that ratings update user's average rating"""
        with app.app_context():
            service = RatingService()
            worker = sample_users['worker']
            
            # Initial average should be 0
            assert worker.average_rating == 0.0 or worker.average_rating is None
            
            # Add a rating
            review = Review(
                reviewer_id=sample_users['contractor'].id,
                reviewee_id=worker.id,
                job_id=sample_contract.job_id,
                contract_id=sample_contract.id,
                overall_rating=5,
                quality_rating=5,
                communication_rating=4,
                safety_rating=5,
                comment='Great work!',
                verified_completion=True
            )
            db.session.add(review)
            db.session.commit()
            
            # Update user's average rating
            avg_rating = service.calculate_average_rating(worker.id)
            worker.average_rating = avg_rating
            db.session.commit()
            
            # Check that average was updated
            assert abs(worker.average_rating - 4.75) < 0.01  # (5+5+4+5)/4 = 4.75
    
    def test_get_user_ratings(self, app, sample_contract, sample_users):
        """Test getting all ratings for a user"""
        with app.app_context():
            service = RatingService()
            worker = sample_users['worker']
            
            # Add some ratings
            review1 = Review(
                reviewer_id=sample_users['contractor'].id,
                reviewee_id=worker.id,
                job_id=sample_contract.job_id,
                contract_id=sample_contract.id,
                overall_rating=5,
                quality_rating=5,
                communication_rating=4,
                safety_rating=5,
                comment='Excellent work!',
                verified_completion=True
            )
            db.session.add(review1)
            db.session.commit()
            
            # Get ratings
            ratings = service.get_user_ratings(worker.id)
            
            assert len(ratings) == 1
            assert ratings[0].overall_rating == 5
            assert ratings[0].comment == 'Excellent work!'
    
    def test_rating_statistics(self, app, sample_users):
        """Test rating statistics calculation"""
        with app.app_context():
            service = RatingService()
            worker = sample_users['worker']
            
            # Create category first
            from app.models.category import Category
            
            category = Category(
                name='Stats Test Category',
                description='Category for statistics testing'
            )
            db.session.add(category)
            db.session.flush()
            
            # Create multiple contracts and ratings
            for i in range(5):
                job = Job(
                    title=f'Stats Test Job {i+1}',
                    description=f'Job {i+1} for stats',
                    contractor_id=sample_users['contractor'].id,
                    category_id=category.id,
                    location='Sydney, NSW',
                    budget_min=1000.00,
                    budget_max=2000.00,
                    status='assigned'
                )
                db.session.add(job)
                db.session.flush()
                
                contract = Contract(
                    job_id=job.id,
                    contractor_id=sample_users['contractor'].id,
                    worker_id=worker.id,
                    agreed_rate=1500.00,
                    rate_type='total',
                    start_date=date.today() - timedelta(days=30),
                    end_date=date.today() - timedelta(days=1),
                    scope_of_work=f'Stats work {i+1}',
                    status='completed'
                )
                db.session.add(contract)
                db.session.flush()
                
                # Ratings: 5, 4, 3, 2, 1
                rating = 5 - i
                review = Review(
                    reviewer_id=sample_users['contractor'].id,
                    reviewee_id=worker.id,
                    job_id=job.id,
                    contract_id=contract.id,
                    overall_rating=rating,
                    quality_rating=rating,
                    communication_rating=rating,
                    safety_rating=rating,
                    comment=f'{rating} star rating',
                    verified_completion=True
                )
                db.session.add(review)
            
            db.session.commit()
            
            # Get statistics
            stats = service.get_rating_statistics(worker.id)
            
            assert stats['total_ratings'] == 5
            assert stats['average_rating'] == 3.0  # (5+4+3+2+1)/5 = 3.0
            assert stats['five_star_count'] == 1
            assert stats['four_star_count'] == 1
            assert stats['three_star_count'] == 1
            assert stats['two_star_count'] == 1
            assert stats['one_star_count'] == 1
