import uuid
from django.db import models
from django.core.validators import MinValueValidator
from django.utils import timezone


class Transfer(models.Model):
    """Main transfer request model"""
    
    STATUS_CHOICES = [
        ('PENDING', 'Pending Approval'),
        ('APPROVED', 'Approved'),
        ('IN_TRANSIT', 'In Transit'),
        ('DELIVERED', 'Delivered'),
        ('RECEIVED', 'Received'),
        ('PARTIALLY_RECEIVED', 'Partially Received'),
        ('REJECTED', 'Rejected'),
        ('CANCELLED', 'Cancelled'),
        ('CHANGES_REQUESTED', 'Changes Requested'),
    ]
    
    PRIORITY_CHOICES = [
        ('LOW', 'Low'),
        ('MEDIUM', 'Medium'),
        ('HIGH', 'High'),
        ('URGENT', 'Urgent'),
    ]
    
    # Identification
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    transfer_number = models.CharField(
        max_length=50, 
        unique=True, 
        db_index=True,
        help_text="Auto-generated: TRF-YYYYMMDD-XXXXX"
    )
    
    # Business & Locations
    business = models.ForeignKey(
        'business.Business',
        on_delete=models.CASCADE,
        related_name='transfers'
    )
    source_location = models.ForeignKey(
        'business.Location',
        on_delete=models.PROTECT,
        related_name='transfers_outgoing'
    )
    destination_location = models.ForeignKey(
        'business.Location',
        on_delete=models.PROTECT,
        related_name='transfers_incoming'
    )
    
    # Status & Priority
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='PENDING',
        db_index=True
    )
    priority = models.CharField(
        max_length=10,
        choices=PRIORITY_CHOICES,
        default='MEDIUM'
    )
    
    # Personnel
    requested_by = models.ForeignKey(
        'authentication.User',
        on_delete=models.PROTECT,
        related_name='transfers_requested'
    )
    approved_by = models.ForeignKey(
        'authentication.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='transfers_approved'
    )
    received_by = models.ForeignKey(
        'authentication.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='transfers_received'
    )
    
    # Timeline
    requested_date = models.DateTimeField(auto_now_add=True)
    approved_date = models.DateTimeField(null=True, blank=True)
    shipped_date = models.DateTimeField(null=True, blank=True)
    expected_delivery_date = models.DateField(null=True, blank=True)
    delivered_date = models.DateTimeField(null=True, blank=True)
    received_date = models.DateTimeField(null=True, blank=True)
    
    # Rejection/Cancellation
    rejected_at = models.DateTimeField(null=True, blank=True)
    rejected_by = models.ForeignKey(
        'authentication.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='transfers_rejected'
    )
    rejection_reason = models.TextField(blank=True)
    
    # Shipping Information
    carrier_name = models.CharField(max_length=200, blank=True)
    tracking_number = models.CharField(max_length=200, blank=True)
    shipping_method = models.CharField(max_length=100, blank=True)
    estimated_cost = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        null=True,
        blank=True
    )
    actual_cost = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        null=True,
        blank=True
    )
    
    # Receipt Information
    has_discrepancies = models.BooleanField(default=False)
    discrepancy_report = models.TextField(blank=True)
    
    # Notes & Documentation
    notes = models.TextField(blank=True)
    internal_notes = models.TextField(
        blank=True,
        help_text="Internal notes not visible to all users"
    )
    attachments = models.JSONField(
        default=list,
        blank=True,
        help_text="File attachments (receipts, photos, etc.)"
    )
    
    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'transfers'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['business', 'status']),
            models.Index(fields=['source_location', 'status']),
            models.Index(fields=['destination_location', 'status']),
            models.Index(fields=['transfer_number']),
            models.Index(fields=['created_at']),
            models.Index(fields=['status', 'priority']),
        ]
        verbose_name = 'Transfer'
        verbose_name_plural = 'Transfers'
    
    def __str__(self):
        return f"Transfer {self.transfer_number}: {self.source_location} → {self.destination_location}"
    
    def save(self, *args, **kwargs):
        # Auto-generate transfer number
        if not self.transfer_number:
            self.transfer_number = self._generate_transfer_number()
        super().save(*args, **kwargs)
    
    def _generate_transfer_number(self):
        """Generate unique transfer number: TRF-YYYYMMDD-XXXXX"""
        today = timezone.now().strftime('%Y%m%d')
        prefix = f"TRF-{today}"
        
        # Get last transfer number for today
        last_transfer = Transfer.objects.filter(
            transfer_number__startswith=prefix
        ).order_by('-transfer_number').first()
        
        if last_transfer:
            last_seq = int(last_transfer.transfer_number.split('-')[-1])
            new_seq = last_seq + 1
        else:
            new_seq = 1
        
        return f"{prefix}-{new_seq:05d}"
    
    # Permission check methods
    
    def can_approve(self, user):
        """Check if user can approve this transfer"""
        if self.status != 'PENDING':
            return False
        
        # Check user permissions
        membership = user.business_memberships.filter(
            business=self.business,
            status='ACTIVE'
        ).first()
        
        if not membership:
            return False
        
        # Owners and managers can approve
        return membership.role.name in ['OWNER', 'ADMIN', 'MANAGER']
    
    def can_reject(self, user):
        """Check if user can reject this transfer"""
        return self.can_approve(user)
    
    def can_ship(self, user):
        """Check if user can mark as shipped"""
        if self.status != 'APPROVED':
            return False
        
        # Must be at source location
        membership = user.business_memberships.filter(
            business=self.business,
            status='ACTIVE'
        ).first()
        
        if not membership:
            return False
        
        # Check location access
        if not membership.can_access_all_locations:
            return membership.accessible_locations.filter(
                id=self.source_location_id
            ).exists()
        
        return True
    
    def can_receive(self, user):
        """Check if user can receive this transfer"""
        if self.status not in ['IN_TRANSIT', 'DELIVERED']:
            return False
        
        # Must be at destination location
        membership = user.business_memberships.filter(
            business=self.business,
            status='ACTIVE'
        ).first()
        
        if not membership:
            return False
        
        # Check location access
        if not membership.can_access_all_locations:
            return membership.accessible_locations.filter(
                id=self.destination_location_id
            ).exists()
        
        return True
    
    # Action methods
    
    def approve(self, user, notes=None):
        """Approve transfer request"""
        if not self.can_approve(user):
            raise PermissionError("User cannot approve this transfer")
        
        self.status = 'APPROVED'
        self.approved_by = user
        self.approved_date = timezone.now()
        
        if notes:
            self.notes = f"{self.notes}\n\nApproval notes: {notes}".strip()
        
        self.save()
        
        # Create event
        TransferEvent.objects.create(
            transfer=self,
            event_type='APPROVED',
            user=user,
            notes=notes or "Transfer approved"
        )
        
        return self
    
    def reject(self, user, reason):
        """Reject transfer request"""
        if not self.can_reject(user):
            raise PermissionError("User cannot reject this transfer")
        
        self.status = 'REJECTED'
        self.rejected_by = user
        self.rejected_at = timezone.now()
        self.rejection_reason = reason
        self.save()
        
        # Create event
        TransferEvent.objects.create(
            transfer=self,
            event_type='REJECTED',
            user=user,
            notes=reason
        )
        
        return self
    
    def ship(self, user, carrier_name=None, tracking_number=None):
        """Mark transfer as shipped"""
        if not self.can_ship(user):
            raise PermissionError("User cannot ship this transfer")
        
        self.status = 'IN_TRANSIT'
        self.shipped_date = timezone.now()
        
        if carrier_name:
            self.carrier_name = carrier_name
        if tracking_number:
            self.tracking_number = tracking_number
        
        self.save()
        
        # Update inventory items status
        self.items.update(status='IN_TRANSIT')
        
        # Create event
        TransferEvent.objects.create(
            transfer=self,
            event_type='SHIPPED',
            user=user,
            notes=f"Shipped via {carrier_name or 'carrier'}"
        )
        
        return self
    
    def mark_delivered(self, user):
        """Mark transfer as delivered"""
        if self.status != 'IN_TRANSIT':
            raise ValueError("Transfer must be in transit")
        
        self.status = 'DELIVERED'
        self.delivered_date = timezone.now()
        self.save()
        
        # Create event
        TransferEvent.objects.create(
            transfer=self,
            event_type='DELIVERED',
            user=user,
            notes="Transfer delivered to destination"
        )
        
        return self
    
    def receive(self, user, items_data, has_discrepancies=False, 
                discrepancy_report=None):
        """
        Receive transfer at destination
        
        items_data: list of dicts with structure:
        [
            {
                'item_id': uuid,
                'received_quantity': int,
                'condition': 'Good'|'Damaged'|'Missing',
                'notes': str (optional)
            },
            ...
        ]
        """
        if not self.can_receive(user):
            raise PermissionError("User cannot receive this transfer")
        
        from django.db import transaction
        
        with transaction.atomic():
            # Update transfer
            self.status = 'RECEIVED' if not has_discrepancies else 'PARTIALLY_RECEIVED'
            self.received_by = user
            self.received_date = timezone.now()
            self.has_discrepancies = has_discrepancies
            
            if discrepancy_report:
                self.discrepancy_report = discrepancy_report
            
            self.save()
            
            # Update transfer items
            for item_data in items_data:
                transfer_item = self.items.get(id=item_data['item_id'])
                transfer_item.received_quantity = item_data['received_quantity']
                transfer_item.condition = item_data['condition']
                transfer_item.received_notes = item_data.get('notes', '')
                transfer_item.save()
                
                # Update inventory location if condition is good
                if item_data['condition'] == 'Good':
                    inventory_item = transfer_item.inventory_item
                    inventory_item.location = self.destination_location
                    inventory_item.status = 'IN_STOCK'
                    inventory_item.save()
            
            # Create event
            TransferEvent.objects.create(
                transfer=self,
                event_type='RECEIVED',
                user=user,
                notes=f"Received {'with discrepancies' if has_discrepancies else 'successfully'}"
            )
        
        return self
    
    # Properties
    
    @property
    def total_items_count(self):
        """Get total number of items in transfer"""
        return self.items.count()
    
    @property
    def total_value(self):
        """Calculate total value of transfer"""
        from django.db.models import Sum, F
        result = self.items.aggregate(
            total=Sum(F('quantity') * F('inventory_item__selling_price'))
        )
        return result['total'] or 0
    
    @property
    def duration_days(self):
        """Calculate transfer duration in days"""
        if self.received_date and self.shipped_date:
            return (self.received_date.date() - self.shipped_date.date()).days
        return None
    
    @property
    def is_overdue(self):
        """Check if transfer is overdue"""
        if self.expected_delivery_date and self.status in ['IN_TRANSIT', 'DELIVERED']:
            return timezone.now().date() > self.expected_delivery_date
        return False


class TransferItem(models.Model):
    """Individual items in a transfer"""
    
    CONDITION_CHOICES = [
        ('Good', 'Good Condition'),
        ('Damaged', 'Damaged'),
        ('Missing', 'Missing'),
    ]
    
    STATUS_CHOICES = [
        ('PENDING', 'Pending'),
        ('IN_TRANSIT', 'In Transit'),
        ('RECEIVED', 'Received'),
        ('DAMAGED', 'Damaged'),
        ('MISSING', 'Missing'),
    ]
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    transfer = models.ForeignKey(
        Transfer,
        on_delete=models.CASCADE,
        related_name='items'
    )
    inventory_item = models.ForeignKey(
        'inventory.InventoryItem',
        on_delete=models.PROTECT,
        related_name='transfer_items'
    )
    
    # Quantities
    quantity = models.IntegerField(
        validators=[MinValueValidator(1)],
        help_text="Quantity being transferred"
    )
    received_quantity = models.IntegerField(
        null=True,
        blank=True,
        validators=[MinValueValidator(0)],
        help_text="Actual quantity received"
    )
    
    # Condition tracking
    condition = models.CharField(
        max_length=20,
        choices=CONDITION_CHOICES,
        default='Good'
    )
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='PENDING'
    )
    
    # Notes
    notes = models.TextField(blank=True)
    received_notes = models.TextField(
        blank=True,
        help_text="Notes added during receiving"
    )
    
    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'transfer_items'
        unique_together = ['transfer', 'inventory_item']
        indexes = [
            models.Index(fields=['transfer', 'status']),
            models.Index(fields=['inventory_item']),
        ]
    
    def __str__(self):
        return f"{self.inventory_item} x {self.quantity}"
    
    @property
    def variance(self):
        """Calculate variance between expected and received"""
        if self.received_quantity is not None:
            return self.received_quantity - self.quantity
        return None
    
    @property
    def has_discrepancy(self):
        """Check if item has receiving discrepancy"""
        return (
            self.received_quantity is not None and 
            (self.received_quantity != self.quantity or 
             self.condition != 'Good')
        )


class TransferComment(models.Model):
    """Comments/notes on transfers"""
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    transfer = models.ForeignKey(
        Transfer,
        on_delete=models.CASCADE,
        related_name='comments'
    )
    user = models.ForeignKey(
        'authentication.User',
        on_delete=models.PROTECT
    )
    
    text = models.TextField()
    is_internal = models.BooleanField(
        default=False,
        help_text="Internal comments visible only to specific roles"
    )
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'transfer_comments'
        ordering = ['created_at']
        indexes = [
            models.Index(fields=['transfer', 'created_at']),
        ]
    
    def __str__(self):
        return f"Comment by {self.user} on {self.transfer.transfer_number}"


class TransferEvent(models.Model):
    """Audit trail for transfer status changes"""
    
    EVENT_TYPE_CHOICES = [
        ('CREATED', 'Transfer Created'),
        ('APPROVED', 'Transfer Approved'),
        ('REJECTED', 'Transfer Rejected'),
        ('SHIPPED', 'Transfer Shipped'),
        ('IN_TRANSIT', 'In Transit Update'),
        ('DELIVERED', 'Transfer Delivered'),
        ('RECEIVED', 'Transfer Received'),
        ('CANCELLED', 'Transfer Cancelled'),
        ('COMMENT_ADDED', 'Comment Added'),
        ('STATUS_CHANGED', 'Status Changed'),
    ]
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    transfer = models.ForeignKey(
        Transfer,
        on_delete=models.CASCADE,
        related_name='events'
    )
    event_type = models.CharField(max_length=20, choices=EVENT_TYPE_CHOICES)
    user = models.ForeignKey(
        'authentication.User',
        on_delete=models.SET_NULL,
        null=True
    )
    
    notes = models.TextField(blank=True)
    metadata = models.JSONField(
        default=dict,
        blank=True,
        help_text="Additional event data"
    )
    
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'transfer_events'
        ordering = ['created_at']
        indexes = [
            models.Index(fields=['transfer', 'created_at']),
            models.Index(fields=['event_type', 'created_at']),
        ]
    
    def __str__(self):
        return f"{self.get_event_type_display()} - {self.transfer.transfer_number}"