#!/usr/bin/env python3
"""
Comprehensive End-to-End Contract Workflow Test
Tests the complete contract lifecycle from creation to completion
"""

import pytest
import requests
from datetime import datetime, date, timedelta
from decimal import Decimal
from app import create_app
from app.extensions import db
from app.models import User, Contract, Job, Category, Application
from app.models.safety import Review
from app.models.contract import Payment, Invoice
from app.services.rating_service import RatingService


class TestContractWorkflowE2E:
    """Comprehensive End-to-End Contract Workflow Testing"""

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

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

    @pytest.fixture
    def test_users(self, app):
        """Create test users for E2E workflow"""
        with app.app_context():
            # Create contractor
            contractor = User(
                email='contractor_e2e@test.com',
                first_name='John',
                last_name='Builder',
                role='contractor',
                phone_number='0400000001',
                location='Sydney, NSW',
                abn_number='12345678901',
                gst_registered=True,
                is_active=True,
                privacy_consent=True,
                terms_accepted=True,
                terms_accepted_date=datetime.utcnow()
            )
            contractor.set_password('contractor123')
            
            # Create worker
            worker = User(
                email='worker_e2e@test.com',
                first_name='Mike',
                last_name='Tradesman',
                role='worker',
                phone_number='0400000002',
                location='Melbourne, VIC',
                abn_number='12345678902',
                gst_registered=False,
                is_active=True,
                privacy_consent=True,
                terms_accepted=True,
                terms_accepted_date=datetime.utcnow()
            )
            worker.set_password('worker123')
            
            db.session.add_all([contractor, worker])
            db.session.commit()
            
            return {
                'contractor': contractor,
                'worker': worker
            }

    @pytest.fixture
    def test_category(self, app):
        """Create test category"""
        with app.app_context():
            category = Category(
                name='E2E Construction',
                description='End-to-end test category',
                whs_risk_level='medium',
                white_card_required=True
            )
            db.session.add(category)
            db.session.commit()
            return category

    def test_complete_contract_workflow_e2e(self, app, client, test_users, test_category):
        """Test complete contract workflow end-to-end"""
        
        print("\n" + "="*80)
        print("🚀 COMPREHENSIVE END-TO-END CONTRACT WORKFLOW TEST")
        print("="*80)
        
        with app.app_context():
            # Re-fetch users to ensure they're attached to current session
            contractor = User.query.filter_by(email='contractor_e2e@test.com').first()
            worker = User.query.filter_by(email='worker_e2e@test.com').first()
            category = Category.query.filter_by(name='E2E Construction').first()
            
            # PHASE 1: CONTRACT CREATION
            print("\n📋 PHASE 1: CONTRACT CREATION")
            print("-" * 40)
            
            # Create job
            job = Job(
                title='E2E Test Construction Job',
                description='Complete bathroom renovation including tiling, plumbing fixtures, and painting',
                contractor_id=contractor.id,
                category_id=category.id,
                location='Sydney, NSW',
                budget_min=5000.00,
                budget_max=8000.00,
                status='open',
                duration='14 days',
                white_card_required=True,
                insurance_required=True,
                workers_needed=1
            )
            db.session.add(job)
            db.session.flush()
            
            print(f"✅ Job created: {job.title}")
            print(f"   ID: {job.id}, Budget: ${job.budget_min}-${job.budget_max}")
            
            # Worker applies for job
            application = Application(
                job_id=job.id,
                worker_id=worker.id,
                cover_letter='I have 10+ years experience in bathroom renovations...',
                proposed_rate=6500.00,
                status='pending'
            )
            db.session.add(application)
            db.session.flush()
            
            print(f"✅ Application submitted by worker")
            print(f"   Proposed rate: ${application.proposed_rate}")
            
            # Contractor accepts application and creates contract
            application.status = 'accepted'
            job.status = 'assigned'
            
            contract = Contract(
                job_id=job.id,
                contractor_id=contractor.id,
                worker_id=worker.id,
                agreed_rate=Decimal('6500.00'),
                rate_type='total',
                start_date=date.today() + timedelta(days=2),
                end_date=date.today() + timedelta(days=16),
                scope_of_work='Complete bathroom renovation as specified in job description',
                status='pending_agreement',
                independent_contractor_status=True,
                superannuation_required=False,
                workers_comp_covered=True,
                payment_terms='completion'
            )
            db.session.add(contract)
            db.session.commit()
            
            print(f"✅ Contract created: ID {contract.id}")
            print(f"   Status: {contract.status}")
            print(f"   Agreed rate: ${contract.agreed_rate}")
            
            # Test contract methods
            assert contract.get_next_step() in ['contractor_review', 'worker_review', 'ready_for_signing']
            assert contract.can_sign(contractor.id) == True
            assert contract.can_sign(worker.id) == True
            assert contract.is_fully_signed() == False
            
            # PHASE 2: CONTRACT REVIEW AND SIGNING
            print("\n✍️ PHASE 2: CONTRACT REVIEW AND SIGNING")
            print("-" * 40)
            
            # Both parties review the contract
            contract.contractor_reviewed = True
            contract.worker_reviewed = True
            db.session.commit()
            
            print("✅ Both parties reviewed contract")
            assert contract.get_next_step() == 'ready_for_signing'
            
            # Contractor signs first
            sign_result = contract.sign_contract(contractor.id)
            assert sign_result == True
            assert contract.contractor_signed == True
            assert contract.status == 'contractor_signed'
            assert contract.contractor_signed_date is not None
            
            print(f"✅ Contractor signed contract")
            print(f"   Status: {contract.status}")
            print(f"   Signed at: {contract.contractor_signed_date}")
            
            # Worker signs second
            sign_result = contract.sign_contract(worker.id)
            assert sign_result == True
            assert contract.worker_signed == True
            assert contract.status == 'active'
            assert contract.worker_signed_date is not None
            
            print(f"✅ Worker signed contract")
            print(f"   Status: {contract.status}")
            print(f"   Contract fully signed: {contract.is_fully_signed()}")
            
            # PHASE 3: PAYMENT SETUP (ESCROW)
            print("\n💰 PHASE 3: PAYMENT SETUP (ESCROW)")
            print("-" * 40)
            
            # Create payment in escrow
            payment = Payment(
                contract_id=contract.id,
                payment_reference=f'RR-E2E-{contract.id}-001',
                gross_amount=contract.agreed_rate,
                platform_fee=contract.agreed_rate * Decimal('0.10'),  # 10% platform fee
                gst_amount=Decimal('0.00'),  # Worker not GST registered
                net_to_worker=contract.agreed_rate * Decimal('0.90'),
                withholding_tax_rate=Decimal('0.00'),
                withholding_tax_amount=Decimal('0.00'),
                status='held_escrow',
                date_initiated=datetime.utcnow(),
                date_held_escrow=datetime.utcnow(),
                dispute_period_days=7,
                dispute_deadline=datetime.utcnow() + timedelta(days=7)
            )
            db.session.add(payment)
            db.session.commit()
            
            print(f"✅ Payment held in escrow")
            print(f"   Reference: {payment.payment_reference}")
            print(f"   Gross amount: ${payment.gross_amount}")
            print(f"   Platform fee: ${payment.platform_fee}")
            print(f"   Net to worker: ${payment.net_to_worker}")
            
            # PHASE 4: WORK EXECUTION
            print("\n� PHASE 4: WORK EXECUTION PHASE")
            print("-" * 40)
            
            # Simulate work progress
            contract.completion_status = 'in_progress'
            db.session.commit()
            
            print(f"✅ Work commenced")
            print(f"   Completion status: {contract.completion_status}")
            print(f"   Start date: {contract.start_date}")
            print(f"   End date: {contract.end_date}")
            
            # Simulate work completion by worker
            contract.completion_status = 'completed_by_worker'
            contract.worker_completion_date = datetime.utcnow()
            contract.status = 'pending_completion_review'  # New long status that now fits!
            db.session.commit()
            
            print(f"✅ Worker marked work as complete")
            print(f"   Status: {contract.status}")
            print(f"   Completion date: {contract.worker_completion_date}")
            
            # PHASE 5: CONTRACTOR REVIEW AND APPROVAL
            print("\n👔 PHASE 5: CONTRACTOR REVIEW AND APPROVAL")
            print("-" * 40)
            
            # Contractor approves the work
            contract.contractor_approval_date = datetime.utcnow()
            contract.status = 'completed'
            contract.completion_status = 'approved_by_contractor'
            db.session.commit()
            
            print(f"✅ Contractor approved work completion")
            print(f"   Status: {contract.status}")
            print(f"   Approval date: {contract.contractor_approval_date}")
            
            # PHASE 6: PAYMENT RELEASE
            print("\n💳 PHASE 6: PAYMENT RELEASE")
            print("-" * 40)
            
            # Release payment from escrow
            payment.status = 'released'
            payment.date_released = datetime.utcnow()
            payment.release_conditions_met = True
            contract.payment_status = 'completed'
            db.session.commit()
            
            print(f"✅ Payment released from escrow")
            print(f"   Payment status: {payment.status}")
            print(f"   Released at: {payment.date_released}")
            print(f"   Amount released: ${payment.net_to_worker}")
            
            # PHASE 7: MUTUAL RATING EXCHANGE
            print("\n⭐ PHASE 7: MUTUAL RATING EXCHANGE")
            print("-" * 40)
            
            # Test rating status before ratings
            assert contract.is_mutual_rating_complete() == False
            assert contract.get_rating_status_for_user(contractor.id) == 'pending'
            assert contract.get_rating_status_for_user(worker.id) == 'pending'
            
            pending_ratings = contract.get_pending_ratings_summary()
            assert 'contractor' in pending_ratings
            assert 'worker' in pending_ratings
            
            print(f"✅ Rating status verified")
            print(f"   Mutual rating complete: {contract.is_mutual_rating_complete()}")
            print(f"   Pending ratings: {pending_ratings}")
            
            # Contractor rates worker
            contractor_review = Review(
                reviewer_id=contractor.id,
                reviewee_id=worker.id,
                job_id=job.id,
                contract_id=contract.id,
                overall_rating=5,
                quality_rating=5,
                communication_rating=5,
                safety_rating=5,
                comment='Excellent work! High quality bathroom renovation completed on time and within budget.',
                would_work_again=True,
                verified_completion=True
            )
            db.session.add(contractor_review)
            contract.contractor_rated = True
            
            print(f"✅ Contractor rated worker: {contractor_review.overall_rating}/5 stars")
            print(f"   Comment: {contractor_review.comment[:50]}...")
            
            # Worker rates contractor
            worker_review = Review(
                reviewer_id=worker.id,
                reviewee_id=contractor.id,
                job_id=job.id,
                contract_id=contract.id,
                overall_rating=4,
                quality_rating=4,
                communication_rating=5,
                safety_rating=4,
                comment='Good contractor to work with. Clear communication and fair payment terms.',
                would_work_again=True,
                verified_completion=True
            )
            db.session.add(worker_review)
            contract.worker_rated = True
            contract.mutual_rating_completed_date = datetime.utcnow()
            
            db.session.commit()
            
            print(f"✅ Worker rated contractor: {worker_review.overall_rating}/5 stars")
            print(f"   Comment: {worker_review.comment[:50]}...")
            
            # Test rating completion
            assert contract.is_mutual_rating_complete() == True
            assert contract.get_rating_status_for_user(contractor.id) == 'completed'
            assert contract.get_rating_status_for_user(worker.id) == 'completed'
            assert len(contract.get_pending_ratings_summary()) == 0
            
            print(f"✅ Mutual rating completed")
            print(f"   Completed date: {contract.mutual_rating_completed_date}")
            
            # PHASE 8: RATING SERVICE VERIFICATION
            print("\n🔢 PHASE 8: RATING SERVICE VERIFICATION")
            print("-" * 40)
            
            # Test rating service calculations
            rating_service = RatingService()
            
            # Test worker's rating
            worker_avg = rating_service.calculate_average_rating(worker.id)
            worker_total = rating_service.get_total_reviews(worker.id)
            worker_stats = rating_service.get_rating_statistics(worker.id)
            
            print(f"✅ Worker rating calculations:")
            print(f"   Average rating: {worker_avg}")
            print(f"   Total reviews: {worker_total}")
            print(f"   Rating breakdown: {worker_stats}")
            
            # Test contractor's rating
            contractor_avg = rating_service.calculate_average_rating(contractor.id)
            contractor_total = rating_service.get_total_reviews(contractor.id)
            contractor_stats = rating_service.get_rating_statistics(contractor.id)
            
            print(f"✅ Contractor rating calculations:")
            print(f"   Average rating: {contractor_avg}")
            print(f"   Total reviews: {contractor_total}")
            print(f"   Rating breakdown: {contractor_stats}")
            
            # Verify calculations are correct
            assert worker_avg == 5.0  # Single 5-star rating
            assert worker_stats['total_ratings'] == 1  # Check statistics instead of total
            assert contractor_avg == 4.0  # Single 4-star rating
            assert contractor_stats['total_ratings'] == 1  # Check statistics instead of total
            
            # PHASE 9: FINAL CONTRACT STATE VERIFICATION
            print("\n✅ PHASE 9: FINAL CONTRACT STATE VERIFICATION")
            print("-" * 40)
            
            # Verify final contract state
            final_verifications = {
                'Contract fully signed': contract.is_fully_signed(),
                'Work completed': contract.completion_status == 'approved_by_contractor',
                'Payment released': payment.status == 'released',
                'Mutual rating complete': contract.is_mutual_rating_complete(),
                'Contract completed': contract.status == 'completed',
                'Payment completed': contract.payment_status == 'completed'
            }
            
            for check, result in final_verifications.items():
                status = "✅" if result else "❌"
                print(f"   {status} {check}: {result}")
                assert result == True, f"Final verification failed: {check}"
            
            # PHASE 10: WORKFLOW METRICS AND SUMMARY
            print("\n📊 PHASE 10: WORKFLOW METRICS AND SUMMARY")
            print("-" * 40)
            
            # Calculate workflow metrics
            total_days = (contract.worker_completion_date.date() - contract.start_date).days
            total_value = float(contract.agreed_rate)
            platform_revenue = float(payment.platform_fee)
            
            workflow_summary = {
                'Contract ID': contract.id,
                'Job Title': job.title,
                'Total Value': f'${total_value:,.2f}',
                'Platform Fee': f'${platform_revenue:,.2f}',
                'Worker Net': f'${float(payment.net_to_worker):,.2f}',
                'Duration': f'{total_days} days',
                'Contractor Rating': f'{contractor_avg}/5',
                'Worker Rating': f'{worker_avg}/5',
                'Workflow Status': 'COMPLETED SUCCESSFULLY'
            }
            
            print("   Workflow Summary:")
            for metric, value in workflow_summary.items():
                print(f"   • {metric}: {value}")
            
            print("\n" + "="*80)
            print("🎉 END-TO-END CONTRACT WORKFLOW TEST COMPLETED SUCCESSFULLY!")
            print("="*80)
            
            # Return final state for additional assertions
            return {
                'contract': contract,
                'payment': payment,
                'contractor_review': contractor_review,
                'worker_review': worker_review,
                'workflow_summary': workflow_summary
            }

    def test_contract_workflow_edge_cases(self, app, client, test_users, test_category):
        """Test contract workflow edge cases and error conditions"""
        
        print("\n" + "="*80)
        print("🧪 CONTRACT WORKFLOW EDGE CASES TEST")
        print("="*80)
        
        with app.app_context():
            # Re-fetch users to ensure they're attached to current session
            contractor = User.query.filter_by(email='contractor_e2e@test.com').first()
            worker = User.query.filter_by(email='worker_e2e@test.com').first()
            category = Category.query.filter_by(name='E2E Construction').first()
            
            # Test 1: Contract with disputed completion
            print("\n🔍 TEST 1: Disputed Completion Workflow")
            print("-" * 40)
            
            # Create disputed contract scenario
            job = Job(
                title='Disputed Work Test Job',
                description='Test job for dispute scenario',
                contractor_id=contractor.id,
                category_id=category.id,
                location='Brisbane, QLD',
                budget_min=2000.00,
                budget_max=3000.00,
                status='assigned'
            )
            db.session.add(job)
            db.session.flush()
            
            disputed_contract = Contract(
                job_id=job.id,
                contractor_id=contractor.id,
                worker_id=worker.id,
                agreed_rate=Decimal('2500.00'),
                rate_type='total',
                start_date=date.today(),
                end_date=date.today() + timedelta(days=5),
                scope_of_work='Test work for dispute',
                status='disputed',  # Long status that now works
                contractor_signed=True,
                worker_signed=True
            )
            db.session.add(disputed_contract)
            db.session.commit()
            
            print(f"✅ Disputed contract created: {disputed_contract.status}")
            
            # Test that disputed contracts can't proceed to rating
            assert disputed_contract.is_fully_signed() == True
            assert disputed_contract.status == 'disputed'
            
            # Test 2: Long status fields
            print("\n📏 TEST 2: Long Status Field Handling")
            print("-" * 40)
            
            long_statuses = [
                'pending_completion_review',
                'awaiting_contractor_approval',
                'disputed_completion_under_review'
            ]
            
            for long_status in long_statuses:
                disputed_contract.status = long_status
                db.session.commit()
                
                # Verify the status was saved correctly
                db.session.refresh(disputed_contract)
                assert disputed_contract.status == long_status
                
                print(f"✅ Long status handled: '{long_status}' ({len(long_status)} chars)")
            
            # Test 3: Rating service edge cases
            print("\n⚠️ TEST 3: Rating Service Edge Cases")
            print("-" * 40)
            
            rating_service = RatingService()
            
            # Test with non-existent user
            non_existent_avg = rating_service.calculate_average_rating(99999)
            assert non_existent_avg == 0.0
            print("✅ Non-existent user handled gracefully")
            
            # Test with invalid user ID
            invalid_avg = rating_service.calculate_average_rating(-1)
            assert invalid_avg == 0.0
            print("✅ Invalid user ID handled gracefully")
            
            # Test with None user ID
            none_avg = rating_service.calculate_average_rating(None)
            assert none_avg == 0.0
            print("✅ None user ID handled gracefully")
            
            print("\n✅ All edge cases handled successfully!")

    def test_contract_workflow_performance(self, app, client, test_users, test_category):
        """Test contract workflow performance with multiple contracts"""
        
        print("\n" + "="*80)
        print("🚀 CONTRACT WORKFLOW PERFORMANCE TEST")
        print("="*80)
        
        with app.app_context():
            # Re-fetch users to ensure they're attached to current session
            contractor = User.query.filter_by(email='contractor_e2e@test.com').first()
            worker = User.query.filter_by(email='worker_e2e@test.com').first()
            category = Category.query.filter_by(name='E2E Construction').first()
            
            import time
            start_time = time.time()
            
            # Create multiple contracts to test performance
            contracts_created = []
            reviews_created = []
            
            for i in range(5):  # Create 5 contracts for performance testing
                # Create job
                job = Job(
                    title=f'Performance Test Job {i+1}',
                    description=f'Performance testing job number {i+1}',
                    contractor_id=contractor.id,
                    category_id=category.id,
                    location='Perth, WA',
                    budget_min=1000.00 + (i * 100),
                    budget_max=2000.00 + (i * 100),
                    status='assigned'
                )
                db.session.add(job)
                db.session.flush()
                
                # Create contract
                contract = Contract(
                    job_id=job.id,
                    contractor_id=contractor.id,
                    worker_id=worker.id,
                    agreed_rate=Decimal(str(1500.00 + (i * 100))),
                    rate_type='total',
                    start_date=date.today(),
                    end_date=date.today() + timedelta(days=7),
                    scope_of_work=f'Performance test work {i+1}',
                    status='completed',
                    contractor_signed=True,
                    worker_signed=True
                )
                db.session.add(contract)
                db.session.flush()
                
                # Create reviews
                contractor_review = Review(
                    reviewer_id=contractor.id,
                    reviewee_id=worker.id,
                    job_id=job.id,
                    contract_id=contract.id,
                    overall_rating=4 + (i % 2),  # Alternate between 4 and 5
                    quality_rating=4 + (i % 2),
                    communication_rating=4 + (i % 2),
                    safety_rating=4 + (i % 2),
                    comment=f'Performance test review {i+1}',
                    verified_completion=True
                )
                db.session.add(contractor_review)
                
                worker_review = Review(
                    reviewer_id=worker.id,
                    reviewee_id=contractor.id,
                    job_id=job.id,
                    contract_id=contract.id,
                    overall_rating=4,
                    quality_rating=4,
                    communication_rating=4,
                    safety_rating=4,
                    comment=f'Worker performance review {i+1}',
                    verified_completion=True
                )
                db.session.add(worker_review)
                
                contracts_created.append(contract)
                reviews_created.extend([contractor_review, worker_review])
            
            db.session.commit()
            
            creation_time = time.time() - start_time
            print(f"✅ Created 5 contracts with reviews in {creation_time:.2f} seconds")
            
            # Test rating service performance
            start_time = time.time()
            
            rating_service = RatingService()
            worker_avg = rating_service.calculate_average_rating(worker.id)
            contractor_avg = rating_service.calculate_average_rating(contractor.id)
            
            worker_stats = rating_service.get_rating_statistics(worker.id)
            contractor_stats = rating_service.get_rating_statistics(contractor.id)
            
            calculation_time = time.time() - start_time
            print(f"✅ Calculated ratings for both users in {calculation_time:.2f} seconds")
            
            # Verify calculations with multiple reviews (tests are isolated)
            expected_worker_total = 5  # 5 from this test (tests are isolated)
            expected_contractor_total = 5
            
            assert worker_stats['total_ratings'] == expected_worker_total
            assert contractor_stats['total_ratings'] == expected_contractor_total
            
            print(f"✅ Worker: {worker_avg}/5 avg from {worker_stats['total_ratings']} reviews")
            print(f"✅ Contractor: {contractor_avg}/5 avg from {contractor_stats['total_ratings']} reviews")
            
            total_time = creation_time + calculation_time
            print(f"\n📊 Total performance test time: {total_time:.2f} seconds")
            print("✅ Performance test completed successfully!")


if __name__ == "__main__":
    # Run all E2E tests
    pytest.main([__file__, "-v", "-s"])
