from django.test import TestCase
from django.urls import reverse

from accounts.models import User

from .models import (
    Asset,
    Branch,
    Category,
    Product,
    ProductStock,
    StockLocation,
    StockMovement,
    StockTransfer,
    UserStoreAssignment,
)


class StorekeeperBranchScopeTests(TestCase):
    def setUp(self):
        self.storekeeper = User.objects.create_user(
            username='keeper',
            password='testpass123',
            role=User.Role.STOREKEEPER,
        )
        self.client.force_login(self.storekeeper)

        self.branch_a = Branch.objects.create(name='Branch A')
        self.branch_b = Branch.objects.create(name='Branch B')

        self.loc_a1 = StockLocation.objects.create(
            branch=self.branch_a,
            name='A Main Store',
            code='A-MAIN',
            is_active=True,
        )
        self.loc_a2 = StockLocation.objects.create(
            branch=self.branch_a,
            name='A Kitchen Store',
            code='A-KITCH',
            is_active=True,
        )
        self.loc_b1 = StockLocation.objects.create(
            branch=self.branch_b,
            name='B Main Store',
            code='B-MAIN',
            is_active=True,
        )
        self.loc_b2 = StockLocation.objects.create(
            branch=self.branch_b,
            name='B Library Store',
            code='B-LIB',
            is_active=True,
        )

        UserStoreAssignment.objects.create(
            user=self.storekeeper,
            location=self.loc_a1,
            is_active=True,
        )

    def test_storekeeper_location_list_forces_assigned_branch(self):
        response = self.client.get(
            reverse('inventory:location_list'),
            {'branch': self.branch_b.id},
        )

        self.assertEqual(response.status_code, 200)
        locations = list(response.context['locations'])
        self.assertIn(self.loc_a1, locations)
        self.assertIn(self.loc_a2, locations)
        self.assertNotIn(self.loc_b1, locations)
        self.assertEqual(response.context['current_branch'], str(self.branch_a.id))

    def test_storekeeper_transfer_detail_blocks_other_branch(self):
        transfer = StockTransfer.objects.create(
            reference='TRN-9001',
            from_location=self.loc_b1,
            to_location=self.loc_b2,
            requested_by=self.storekeeper,
        )

        response = self.client.get(reverse('inventory:transfer_detail', args=[transfer.pk]))
        self.assertEqual(response.status_code, 403)

    def test_storekeeper_transfer_form_locations_are_scoped_to_assigned_branch(self):
        response = self.client.get(reverse('inventory:transfer_create'))

        self.assertEqual(response.status_code, 200)
        form = response.context['form']
        self.assertTrue(form.fields['branch'].disabled)

        from_location_ids = set(form.fields['from_location'].queryset.values_list('id', flat=True))
        self.assertEqual(from_location_ids, {self.loc_a1.id, self.loc_a2.id})
        self.assertNotIn(self.loc_b1.id, from_location_ids)

    def test_storekeeper_low_stock_report_forces_assigned_branch(self):
        category = Category.objects.create(name='Consumables')
        product_a = Product.objects.create(
            category=category,
            name='Soap A',
            sku='A-SOAP',
            low_stock_alert=10,
            stock_quantity=5,
            is_active=True,
        )
        product_b = Product.objects.create(
            category=category,
            name='Soap B',
            sku='B-SOAP',
            low_stock_alert=10,
            stock_quantity=1,
            is_active=True,
        )
        ProductStock.objects.create(product=product_a, location=self.loc_a1, quantity=5)
        ProductStock.objects.create(product=product_b, location=self.loc_b1, quantity=1)

        response = self.client.get(
            reverse('inventory:low_stock_report'),
            {'branch': self.branch_b.id},
        )

        self.assertEqual(response.status_code, 200)
        products = list(response.context['products'])
        self.assertIn(product_a, products)
        self.assertNotIn(product_b, products)
        self.assertEqual(response.context['current_branch'], str(self.branch_a.id))

    def test_storekeeper_cannot_access_branch_management_views(self):
        create_response = self.client.get(reverse('inventory:branch_create'))
        edit_response = self.client.get(reverse('inventory:branch_edit', args=[self.branch_a.pk]))

        self.assertEqual(create_response.status_code, 403)
        self.assertEqual(edit_response.status_code, 403)

    def test_storekeeper_asset_list_forces_assigned_branch(self):
        asset_a = Asset.objects.create(name='Asset A', code='AS-A', location=self.loc_a1)
        asset_b = Asset.objects.create(name='Asset B', code='AS-B', location=self.loc_b1)

        response = self.client.get(
            reverse('inventory:asset_list'),
            {'branch': self.branch_b.id},
        )

        self.assertEqual(response.status_code, 200)
        assets = list(response.context['assets'])
        self.assertIn(asset_a, assets)
        self.assertNotIn(asset_b, assets)
        self.assertEqual(response.context['current_branch'], str(self.branch_a.id))

    def test_storekeeper_asset_detail_blocks_other_branch(self):
        asset_b = Asset.objects.create(name='Asset B', code='AS-B', location=self.loc_b1)

        response = self.client.get(reverse('inventory:asset_detail', args=[asset_b.pk]))
        self.assertEqual(response.status_code, 403)

    def test_storekeeper_product_detail_shows_only_assigned_branch_movements(self):
        category = Category.objects.create(name='Scope Category')
        product = Product.objects.create(
            category=category,
            name='Scoped Product',
            sku='SCOPE-001',
            stock_quantity=10,
            is_active=True,
        )
        move_a = StockMovement.objects.create(
            product=product,
            location=self.loc_a1,
            movement_type=StockMovement.MovementType.STOCK_IN,
            quantity=3,
            created_by=self.storekeeper,
        )
        move_b = StockMovement.objects.create(
            product=product,
            location=self.loc_b1,
            movement_type=StockMovement.MovementType.STOCK_IN,
            quantity=4,
            created_by=self.storekeeper,
        )

        response = self.client.get(reverse('inventory:product_detail', args=[product.pk]))
        self.assertEqual(response.status_code, 200)
        movements = list(response.context['movements'])
        self.assertIn(move_a, movements)
        self.assertNotIn(move_b, movements)
