import io
from decimal import Decimal

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

from accounts.models import UserRole
from core.freight_import import bulk_upsert_freight_routes, parse_freight_route_upload
from core.models import FreightRoute

User = get_user_model()


def _admin_client(email):
    admin = User.objects.create_user(email, email=email, password='secret')
    admin.is_staff = True
    admin.is_superuser = True
    admin.save()
    UserRole.objects.create(user=admin, role=UserRole.Role.ADMIN, phone='', language='en')
    token, _ = Token.objects.get_or_create(user=admin)
    client = APIClient()
    client.credentials(HTTP_AUTHORIZATION=f'Bearer {token.key}')
    return client


class FreightRouteModelTests(TestCase):
    def setUp(self):
        FreightRoute.objects.filter(
            origin_country_code__in=['PK', 'CN'],
            destination_country_code__in=['PK', 'CN'],
        ).delete()

    def test_directional_routes_are_independent(self):
        FreightRoute.objects.create(
            origin_country_code='PK',
            destination_country_code='CN',
            min_freight=Decimal('1000.00'),
        )
        FreightRoute.objects.create(
            origin_country_code='CN',
            destination_country_code='PK',
            min_freight=Decimal('1200.00'),
        )
        self.assertEqual(
            FreightRoute.objects.filter(
                origin_country_code__in=['PK', 'CN'],
                destination_country_code__in=['PK', 'CN'],
            ).count(),
            2,
        )

    def test_duplicate_pair_rejected_cleanly_via_api(self):
        FreightRoute.objects.create(
            origin_country_code='PK',
            destination_country_code='CN',
            min_freight=Decimal('1000.00'),
        )
        client = _admin_client('freight-admin@test.com')

        response = client.post('/api/admin/freight-routes/', {
            'origin_country_code': 'PK',
            'destination_country_code': 'CN',
            'min_freight': '999.00',
            'currency': 'USD',
        }, format='json')

        self.assertEqual(response.status_code, 400)
        self.assertIn('already exists', str(response.json()['error']).lower())

    def test_same_origin_destination_rejected(self):
        client = _admin_client('freight-admin2@test.com')

        response = client.post('/api/admin/freight-routes/', {
            'origin_country_code': 'PK',
            'destination_country_code': 'PK',
            'min_freight': '500.00',
            'currency': 'USD',
        }, format='json')

        self.assertEqual(response.status_code, 400)


    def test_create_sets_currency_from_origin_country(self):
        client = _admin_client('freight-admin3@test.com')

        response = client.post('/api/admin/freight-routes/', {
            'origin_country_code': 'PK',
            'destination_country_code': 'CN',
            'min_freight': '1000.00',
            'currency': 'USD',
        }, format='json')

        self.assertEqual(response.status_code, 201)
        data = response.json()['data']
        self.assertEqual(data['currency'], 'PKR')
        route = FreightRoute.objects.get(pk=data['id'])
        self.assertEqual(route.currency, 'PKR')


class FreightRouteBulkImportTests(TestCase):
    def setUp(self):
        FreightRoute.objects.all().delete()

    def test_csv_upsert_and_per_row_errors(self):
        FreightRoute.objects.create(
            origin_country_code='PK',
            destination_country_code='CN',
            min_freight=Decimal('1000.00'),
            currency='USD',
        )
        csv_content = (
            'origin_code,destination_code,min_freight,currency\n'
            'PK,CN,1500.00,USD\n'
            'CN,PK,1100.50,USD\n'
            'PK,PK,100.00,USD\n'
            'ZZ,CN,200.00,USD\n'
        )
        uploaded = io.BytesIO(csv_content.encode('utf-8'))
        uploaded.name = 'rates.csv'
        rows = parse_freight_route_upload(uploaded)
        summary = bulk_upsert_freight_routes(rows)

        self.assertEqual(summary['created'], 1)
        self.assertEqual(summary['updated'], 1)
        self.assertEqual(len(summary['errors']), 2)
        route = FreightRoute.objects.get(
            origin_country_code='PK',
            destination_country_code='CN',
        )
        self.assertEqual(route.min_freight, Decimal('1500.00'))

    def test_rejects_unknown_codes_without_creating_country_rows(self):
        csv_content = (
            'origin_code,destination_code,min_freight,currency\n'
            'AF,IR,800.00,USD\n'
            'XX,YY,100.00,USD\n'
        )
        uploaded = io.BytesIO(csv_content.encode('utf-8'))
        uploaded.name = 'rates.csv'
        rows = parse_freight_route_upload(uploaded)
        summary = bulk_upsert_freight_routes(rows)
        self.assertEqual(summary['created'], 1)
        self.assertEqual(len(summary['errors']), 1)
        self.assertTrue(
            FreightRoute.objects.filter(
                origin_country_code='AF',
                destination_country_code='IR',
            ).exists()
        )


class FreightRouteAdminApiListTests(TestCase):
    def setUp(self):
        FreightRoute.objects.all().delete()
        FreightRoute.objects.create(
            origin_country_code='PK',
            destination_country_code='CN',
            min_freight=Decimal('1000'),
        )
        FreightRoute.objects.create(
            origin_country_code='CN',
            destination_country_code='IN',
            min_freight=Decimal('900'),
        )
        self.client = _admin_client('fr-list@test.com')

    def test_filter_by_origin_code_and_search(self):
        r = self.client.get('/api/admin/freight-routes/', {'origin': 'PK'})
        self.assertEqual(r.status_code, 200)
        results = r.json()['data']['results']
        self.assertEqual(len(results), 1)
        self.assertEqual(results[0]['origin_country_code'], 'PK')
        self.assertEqual(results[0]['origin_country_name'], 'Pakistan')

        r2 = self.client.get('/api/admin/freight-routes/', {'search': 'China'})
        self.assertEqual(r2.status_code, 200)
        self.assertGreaterEqual(r2.json()['data']['total'], 1)

    def test_countries_list_from_countries_module(self):
        r = self.client.get('/api/admin/countries/')
        self.assertEqual(r.status_code, 200)
        codes = {item['code'] for item in r.json()['data']}
        self.assertTrue({'PK', 'CN', 'IN', 'AF', 'IR'} <= codes)
        self.assertNotIn('id', r.json()['data'][0])
