from datetime import timedelta
from decimal import Decimal

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.utils import timezone
from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient

from accounts.models import TransporterDriverLink, TransporterProfile, UserRole
from core.models import Bid, ProofOfDelivery, Shipment, Trip, TripReview

User = get_user_model()


class IndividualDriverStatsApiTests(TestCase):
    def setUp(self):
        self.shipper = User.objects.create_user(
            'ship-ind-stats@test.com',
            email='ship-ind-stats@test.com',
            password='secret',
        )
        UserRole.objects.create(user=self.shipper, role=UserRole.Role.SHIPPER, phone='', language='en')

        self.driver = User.objects.create_user(
            'driver-ind-stats@test.com',
            email='driver-ind-stats@test.com',
            password='secret',
        )
        UserRole.objects.create(user=self.driver, role=UserRole.Role.TRANSPORTER, phone='', language='en')
        TransporterProfile.objects.create(
            user=self.driver,
            account_type=TransporterProfile.AccountType.DRIVER,
            local=True,
            country_to_country=False,
        )

        self.fleet_owner = User.objects.create_user(
            'fleet-ind-stats@test.com',
            email='fleet-ind-stats@test.com',
            password='secret',
        )
        UserRole.objects.create(user=self.fleet_owner, role=UserRole.Role.TRANSPORTER, phone='', language='en')
        TransporterProfile.objects.create(
            user=self.fleet_owner,
            account_type=TransporterProfile.AccountType.FLEET_OWNER,
            local=True,
            country_to_country=False,
        )

        token, _ = Token.objects.get_or_create(user=self.driver)
        self.client = APIClient()
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token.key}')

    def _completed_trip(self, *, pickup_scheduled_at=None, delivered_at=None, with_pod=True):
        shipment = Shipment.objects.create(
            shipper=self.shipper,
            pickup_address='A',
            delivery_address='B',
            cargo_type='General',
            weight='1 ton',
            vehicle_type_required='Flatbed',
            local=True,
            country_to_country=False,
            status=Shipment.Status.COMPLETED,
            pickup_scheduled_at=pickup_scheduled_at,
        )
        bid = Bid.objects.create(
            shipment=shipment,
            transporter=self.driver,
            amount=Decimal('100.00'),
            status=Bid.Status.ACCEPTED,
        )
        trip = Trip.objects.create(
            shipment=shipment,
            accepted_bid=bid,
            transporter=self.driver,
            status=Trip.Status.COMPLETED,
        )
        if with_pod:
            ProofOfDelivery.objects.create(
                trip=trip,
                receiver_name='Receiver',
                delivered_at=delivered_at or timezone.now(),
            )
        return trip

    def _trip_assigned(self):
        shipment = Shipment.objects.create(
            shipper=self.shipper,
            pickup_address='A',
            delivery_address='B',
            cargo_type='General',
            weight='1 ton',
            vehicle_type_required='Flatbed',
            local=True,
            country_to_country=False,
            status=Shipment.Status.ASSIGNED,
        )
        bid = Bid.objects.create(
            shipment=shipment,
            transporter=self.driver,
            amount=Decimal('100.00'),
            status=Bid.Status.ACCEPTED,
        )
        return Trip.objects.create(
            shipment=shipment,
            accepted_bid=bid,
            transporter=self.driver,
            status=Trip.Status.ASSIGNED,
        )

    def _review(self, trip, rating, comment=''):
        return TripReview.objects.create(
            trip=trip,
            shipper=self.shipper,
            transporter=self.driver,
            driver=self.driver,
            transporter_rating=rating,
            comment=comment,
        )

    def test_returns_completed_count_average_rating_on_time_and_ratings_desc(self):
        scheduled = timezone.now() - timedelta(days=2)
        on_time_trip = self._completed_trip(
            pickup_scheduled_at=scheduled,
            delivered_at=scheduled + timedelta(hours=1),
        )
        late_trip = self._completed_trip(
            pickup_scheduled_at=scheduled,
            delivered_at=scheduled + timedelta(days=2),
        )
        self._completed_trip(with_pod=False)
        self._trip_assigned()

        review_older = self._review(on_time_trip, 5, 'Excellent')
        review_newer = self._review(late_trip, 3, 'Late')

        r = self.client.get('/api/transporter/my-stats/')
        self.assertEqual(r.status_code, 200)
        data = r.json()['data']

        self.assertEqual(data['completed_trips_count'], 3)
        self.assertEqual(data['average_rating'], 4.0)
        self.assertEqual(data['review_count'], 2)
        self.assertEqual(data['on_time_delivery_percentage'], 50.0)
        self.assertEqual(data['on_time_deliveries_count'], 1)
        self.assertEqual(data['on_time_eligible_trips_count'], 2)

        ratings = data['ratings']
        self.assertEqual(len(ratings), 2)
        self.assertEqual(ratings[0]['id'], review_newer.id)
        self.assertEqual(ratings[1]['id'], review_older.id)
        self.assertGreaterEqual(ratings[0]['created_at'], ratings[1]['created_at'])
        self.assertEqual(ratings[0]['rating'], 3)
        self.assertIn('shipper_name', ratings[0])

    def test_includes_reviews_where_driver_is_rated_not_transporter(self):
        fleet_owner = User.objects.create_user(
            'fleet-ratings@test.com',
            email='fleet-ratings@test.com',
            password='secret',
        )
        UserRole.objects.create(user=fleet_owner, role=UserRole.Role.TRANSPORTER, phone='', language='en')
        TransporterProfile.objects.create(
            user=fleet_owner,
            account_type=TransporterProfile.AccountType.FLEET_OWNER,
            local=True,
            country_to_country=False,
        )
        shipment = Shipment.objects.create(
            shipper=self.shipper,
            pickup_address='A',
            delivery_address='B',
            cargo_type='General',
            weight='1 ton',
            vehicle_type_required='Flatbed',
            local=True,
            country_to_country=False,
            status=Shipment.Status.COMPLETED,
        )
        bid = Bid.objects.create(
            shipment=shipment,
            transporter=fleet_owner,
            amount=Decimal('100.00'),
            status=Bid.Status.ACCEPTED,
        )
        trip = Trip.objects.create(
            shipment=shipment,
            accepted_bid=bid,
            transporter=fleet_owner,
            assigned_driver=self.driver,
            status=Trip.Status.COMPLETED,
        )
        review = TripReview.objects.create(
            trip=trip,
            shipper=self.shipper,
            transporter=fleet_owner,
            driver=self.driver,
            transporter_rating=4,
            driver_rating=5,
            comment='Great driver',
        )

        r = self.client.get('/api/transporter/my-stats/')
        self.assertEqual(r.status_code, 200)
        data = r.json()['data']
        self.assertEqual(data['review_count'], 1)
        self.assertEqual(data['average_rating'], 5.0)
        self.assertEqual(len(data['ratings']), 1)
        self.assertEqual(data['ratings'][0]['id'], review.id)
        self.assertEqual(data['ratings'][0]['rating'], 5)
        self.assertEqual(data['ratings'][0]['comment'], 'Great driver')

    def test_includes_reviews_for_trips_user_transporter_even_if_review_fks_differ(self):
        """Match reviews via trip.transporter when review transporter/driver FKs differ."""
        other_user = User.objects.create_user(
            'other-ratings@test.com',
            email='other-ratings@test.com',
            password='secret',
        )
        trip = self._completed_trip()
        review = TripReview.objects.create(
            trip=trip,
            shipper=self.shipper,
            transporter=other_user,
            driver=other_user,
            transporter_rating=5,
            driver_rating=5,
            comment='Trip owner rating',
        )

        r = self.client.get('/api/transporter/my-stats/')
        self.assertEqual(r.status_code, 200)
        data = r.json()['data']
        self.assertEqual(data['review_count'], 1)
        self.assertEqual(data['average_rating'], 5.0)
        self.assertEqual(data['ratings'][0]['id'], review.id)

    def test_does_not_include_reviews_for_unrelated_trips(self):
        """Review on another transporter's trip must not appear in this driver's stats."""
        other = User.objects.create_user(
            'other-trip@test.com',
            email='other-trip@test.com',
            password='secret',
        )
        UserRole.objects.create(user=other, role=UserRole.Role.TRANSPORTER, phone='', language='en')
        TransporterProfile.objects.create(
            user=other,
            account_type=TransporterProfile.AccountType.FLEET_OWNER,
            local=True,
            country_to_country=False,
        )
        shipment = Shipment.objects.create(
            shipper=self.shipper,
            pickup_address='A',
            delivery_address='B',
            cargo_type='General',
            weight='1 ton',
            vehicle_type_required='Flatbed',
            local=True,
            country_to_country=False,
            status=Shipment.Status.COMPLETED,
        )
        bid = Bid.objects.create(
            shipment=shipment,
            transporter=other,
            amount=Decimal('100.00'),
            status=Bid.Status.ACCEPTED,
        )
        third_driver = User.objects.create_user(
            'third-trip@test.com',
            email='third-trip@test.com',
            password='secret',
        )
        trip = Trip.objects.create(
            shipment=shipment,
            accepted_bid=bid,
            transporter=other,
            assigned_driver=third_driver,
            status=Trip.Status.COMPLETED,
        )
        TripReview.objects.create(
            trip=trip,
            shipper=self.shipper,
            transporter=other,
            driver=other,
            transporter_rating=5,
            driver_rating=5,
            comment='Not for self.driver',
        )
        self._completed_trip()

        r = self.client.get('/api/transporter/my-stats/')
        data = r.json()['data']
        self.assertEqual(data['completed_trips_count'], 1)
        self.assertEqual(data['review_count'], 0)
        self.assertEqual(data['ratings'], [])

    def test_empty_stats(self):
        r = self.client.get('/api/transporter/my-stats/')
        self.assertEqual(r.status_code, 200)
        data = r.json()['data']
        self.assertEqual(data['completed_trips_count'], 0)
        self.assertIsNone(data['average_rating'])
        self.assertIsNone(data['on_time_delivery_percentage'])
        self.assertEqual(data['ratings'], [])

    def test_fleet_owner_forbidden(self):
        token, _ = Token.objects.get_or_create(user=self.fleet_owner)
        client = APIClient()
        client.credentials(HTTP_AUTHORIZATION=f'Bearer {token.key}')
        r = client.get('/api/transporter/my-stats/')
        self.assertEqual(r.status_code, 403)

    def test_fleet_linked_driver_forbidden(self):
        linked_driver = User.objects.create_user(
            'linked-ind-stats@test.com',
            email='linked-ind-stats@test.com',
            password='secret',
        )
        UserRole.objects.create(user=linked_driver, role=UserRole.Role.TRANSPORTER, phone='', language='en')
        TransporterProfile.objects.create(
            user=linked_driver,
            account_type=TransporterProfile.AccountType.DRIVER,
            local=True,
            country_to_country=False,
        )
        TransporterDriverLink.objects.create(
            transporter=self.fleet_owner,
            driver=linked_driver,
            is_active=True,
        )
        token, _ = Token.objects.get_or_create(user=linked_driver)
        client = APIClient()
        client.credentials(HTTP_AUTHORIZATION=f'Bearer {token.key}')
        r = client.get('/api/transporter/my-stats/')
        self.assertEqual(r.status_code, 403)
