from decimal import Decimal
from django.shortcuts import render
from django.db import transaction
from django.db.models import Sum, Q
from django.shortcuts import get_object_or_404

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView

from .models import Brand, Customer, CreditPayment, Expense, Product, Sale, ShopSettings
from .serializers import (
    BrandSerializer, CustomerSerializer, CreditPaymentSerializer,
    DashboardSerializer, ExpenseSerializer, ProductSerializer, SaleSerializer,
)


# ── Token auth helper ─────────────────────────────────────────────────────────

def get_token(request):
    auth = request.headers.get("Authorization", "")
    return auth[6:].strip() if auth.startswith("Token ") else None


def require_token(request):
    token = get_token(request)
    if not token:
        return Response({"detail": "Authentication required."}, status=401)
    s = ShopSettings.get()
    if not s.token or s.token != token:
        return Response({"detail": "Invalid or expired token."}, status=401)
    return None


# ── Auth ──────────────────────────────────────────────────────────────────────

@api_view(["POST"])
def verify_pin(request):
    """POST /api/auth/verify/ — public."""
    raw = str(request.data.get("pin", "")).strip()
    if not raw:
        return Response({"detail": "PIN is required."}, status=400)
    s = ShopSettings.get()
    if not s.verify_pin(raw):
        return Response({"detail": "Incorrect PIN."}, status=401)
    return Response({"token": s.rotate_token(), "must_change_pin": s.pin_is_default})


@api_view(["POST"])
def change_pin(request):
    """POST /api/auth/change-pin/ — requires valid token."""
    if err := require_token(request): return err
    current = str(request.data.get("current_pin", "")).strip()
    new_pin = str(request.data.get("new_pin", "")).strip()
    if not current or not new_pin:
        return Response({"detail": "current_pin and new_pin are required."}, status=400)
    if len(new_pin) != 4 or not new_pin.isdigit():
        return Response({"detail": "PIN must be exactly 4 digits."}, status=400)
    if new_pin == current:
        return Response({"detail": "New PIN must differ from the current PIN."}, status=400)
    s = ShopSettings.get()
    if not s.verify_pin(current):
        return Response({"detail": "Current PIN is incorrect."}, status=401)
    s.set_pin(new_pin)
    return Response({"detail": "PIN changed successfully.", "token": s.rotate_token()})


@api_view(["POST"])
def logout(request):
    """POST /api/auth/logout/ — requires valid token."""
    if err := require_token(request): return err
    ShopSettings.get().invalidate_token()
    return Response({"detail": "Logged out."})

def home(request):
    template_name = 'index.html'
    return render(request, template_name,{})

# ── Dashboard ─────────────────────────────────────────────────────────────────

class DashboardView(APIView):
    def get(self, request):
        if err := require_token(request): return err
        products = Product.objects.all()
        sales    = Sale.objects.all()
        expenses = Expense.objects.all()

        total_full  = products.aggregate(t=Sum('full_qty'))['t']  or 0
        total_empty = products.aggregate(t=Sum('empty_qty'))['t'] or 0
        total_revenue      = sales.aggregate(t=Sum('paid'))['t']  or Decimal('0')
        outstanding_credit = sales.filter(credit__gt=0).aggregate(t=Sum('credit'))['t'] or Decimal('0')
        total_expenses     = expenses.aggregate(t=Sum('amount'))['t'] or Decimal('0')

        breakdown = [
            {'category': cat, 'total': str(total)}
            for cat, _ in Expense.CATEGORY_CHOICES
            if (total := expenses.filter(category=cat).aggregate(t=Sum('amount'))['t'])
        ]

        return Response(DashboardSerializer({
            'total_full': total_full, 'total_empty': total_empty,
            'total_revenue': total_revenue, 'outstanding_credit': outstanding_credit,
            'total_expenses': total_expenses, 'net_profit': total_revenue - total_expenses,
            'product_count': products.count(), 'customer_count': Customer.objects.count(),
            'sale_count': sales.count(), 'expense_breakdown': breakdown,
        }).data)


# ── Brands ────────────────────────────────────────────────────────────────────

class BrandListCreateView(APIView):
    def get(self, request):
        if err := require_token(request): return err
        Brand.ensure_predefined()           # lazily creates predefined if missing
        return Response(BrandSerializer(Brand.objects.all(), many=True).data)

    def post(self, request):
        if err := require_token(request): return err
        # Check for existing brand (case-insensitive)
        name = request.data.get('name', '').strip()
        existing = Brand.objects.filter(name__iexact=name).first()
        if existing:
            return Response(BrandSerializer(existing).data, status=200)
        serializer = BrandSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)


class BrandDetailView(APIView):
    def get_object(self, pk):
        return get_object_or_404(Brand, pk=pk)

    def get(self, request, pk):
        if err := require_token(request): return err
        return Response(BrandSerializer(self.get_object(pk)).data)

    def patch(self, request, pk):
        if err := require_token(request): return err
        brand = self.get_object(pk)
        if brand.is_predefined:
            return Response({"detail": "Predefined brands cannot be renamed."}, status=400)
        s = BrandSerializer(brand, data=request.data, partial=True)
        if s.is_valid():
            s.save()
            return Response(s.data)
        return Response(s.errors, status=400)

    def delete(self, request, pk):
        if err := require_token(request): return err
        brand = self.get_object(pk)
        if brand.is_predefined:
            return Response({"detail": "Predefined brands cannot be deleted."}, status=400)
        if brand.products.exists():
            return Response({"detail": "Cannot delete a brand that has products."}, status=400)
        brand.delete()
        return Response(status=204)


# ── Products ──────────────────────────────────────────────────────────────────

class ProductListCreateView(APIView):
    def get(self, request):
        if err := require_token(request): return err
        qs = Product.objects.select_related('brand').all()
        if (s := request.query_params.get('state')) == 'full':
            qs = qs.filter(full_qty__gt=0)
        elif s == 'empty':
            qs = qs.filter(empty_qty__gt=0)
        return Response(ProductSerializer(qs, many=True).data)

    def post(self, request):
        if err := require_token(request): return err
        s = ProductSerializer(data=request.data)
        if s.is_valid():
            s.save()
            return Response(s.data, status=201)
        return Response(s.errors, status=400)


class ProductDetailView(APIView):
    def get_object(self, pk):
        return get_object_or_404(Product, pk=pk)

    def get(self, request, pk):
        if err := require_token(request): return err
        return Response(ProductSerializer(self.get_object(pk)).data)

    def patch(self, request, pk):
        if err := require_token(request): return err
        s = ProductSerializer(self.get_object(pk), data=request.data, partial=True)
        if s.is_valid():
            s.save()
            return Response(s.data)
        return Response(s.errors, status=400)

    def delete(self, request, pk):
        if err := require_token(request): return err
        self.get_object(pk).delete()
        return Response(status=204)


@api_view(['PATCH'])
def adjust_quantity(request, pk):
    """PATCH /api/products/<id>/adjust/ — supports "+5", "-3", or absolute int."""
    if err := require_token(request): return err
    product = get_object_or_404(Product, pk=pk)
    field   = request.data.get('field')
    value   = request.data.get('value')
    if field not in ('full_qty', 'empty_qty'):
        return Response({'error': 'field must be full_qty or empty_qty'}, status=400)
    current = getattr(product, field)
    raw = str(value).strip()
    try:
        new_val = (current + int(raw[1:])) if raw.startswith('+') else \
                  (current - int(raw[1:])) if raw.startswith('-') else int(raw)
    except (ValueError, IndexError):
        return Response({'error': f'Invalid value: {raw}'}, status=400)
    if new_val < 0:
        return Response({'error': 'Quantity cannot be negative'}, status=400)
    setattr(product, field, new_val)
    product.save()
    return Response(ProductSerializer(product).data)


# ── Customers ─────────────────────────────────────────────────────────────────

class CustomerListCreateView(APIView):
    def get(self, request):
        if err := require_token(request): return err
        qs = Customer.objects.all()
        if (q := request.query_params.get('search')):
            qs = qs.filter(Q(name__icontains=q) | Q(contact__icontains=q))
        return Response(CustomerSerializer(qs, many=True).data)

    def post(self, request):
        if err := require_token(request): return err
        s = CustomerSerializer(data=request.data)
        if s.is_valid():
            s.save()
            return Response(s.data, status=201)
        return Response(s.errors, status=400)


class CustomerDetailView(APIView):
    def get_object(self, pk):
        return get_object_or_404(Customer, pk=pk)

    def get(self, request, pk):
        if err := require_token(request): return err
        c = self.get_object(pk)
        data = CustomerSerializer(c).data
        data['sales'] = SaleSerializer(c.sales.all(), many=True).data
        return Response(data)

    def patch(self, request, pk):
        if err := require_token(request): return err
        s = CustomerSerializer(self.get_object(pk), data=request.data, partial=True)
        if s.is_valid():
            s.save()
            return Response(s.data)
        return Response(s.errors, status=400)

    def delete(self, request, pk):
        if err := require_token(request): return err
        self.get_object(pk).delete()
        return Response(status=204)


# ── Sales ─────────────────────────────────────────────────────────────────────

class SaleListCreateView(APIView):
    def get(self, request):
        if err := require_token(request): return err
        qs = Sale.objects.select_related('customer', 'product__brand').all()
        if request.query_params.get('credit') == 'true':
            qs = qs.filter(credit__gt=0)
        if (cid := request.query_params.get('customer')):
            qs = qs.filter(customer_id=cid)
        return Response(SaleSerializer(qs, many=True).data)

    @transaction.atomic
    def post(self, request):
        if err := require_token(request): return err
        s = SaleSerializer(data=request.data)
        if not s.is_valid():
            return Response(s.errors, status=400)
        product = s.validated_data['product']
        qty     = s.validated_data.get('qty', 1)
        product.full_qty  -= qty
        product.empty_qty += qty
        product.save()
        return Response(SaleSerializer(s.save()).data, status=201)


class SaleDetailView(APIView):
    def get_object(self, pk):
        return get_object_or_404(Sale, pk=pk)

    def get(self, request, pk):
        if err := require_token(request): return err
        return Response(SaleSerializer(self.get_object(pk)).data)

    def patch(self, request, pk):
        if err := require_token(request): return err
        sale = self.get_object(pk)
        s = SaleSerializer(sale, data=request.data, partial=True)
        if s.is_valid():
            s.save()
            return Response(SaleSerializer(sale).data)
        return Response(s.errors, status=400)


@api_view(['POST'])
@transaction.atomic
def record_credit_payment(request, pk):
    if err := require_token(request): return err
    sale   = get_object_or_404(Sale, pk=pk)
    amount = Decimal(str(request.data.get('amount', 0)))
    if amount <= 0:
        return Response({'error': 'Amount must be greater than 0'}, status=400)
    if amount > sale.credit:
        return Response({'error': f'Amount exceeds outstanding credit of {sale.credit}'}, status=400)
    payment    = CreditPayment.objects.create(sale=sale, amount=amount, note=request.data.get('note', ''))
    sale.paid += amount
    sale.credit = max(Decimal('0'), sale.amount - sale.paid)
    sale.save()
    return Response({'payment': CreditPaymentSerializer(payment).data, 'sale': SaleSerializer(sale).data}, status=201)


# ── Expenses ──────────────────────────────────────────────────────────────────

class ExpenseListCreateView(APIView):
    def get(self, request):
        if err := require_token(request): return err
        qs = Expense.objects.select_related('refill_brand').all()
        if cat := request.query_params.get('category'):
            qs = qs.filter(category=cat)
        return Response(ExpenseSerializer(qs, many=True).data)

    @transaction.atomic
    def post(self, request):
        if err := require_token(request): return err
        s = ExpenseSerializer(data=request.data)
        if not s.is_valid():
            return Response(s.errors, status=400)

        expense = s.save()

        if expense.is_refill and expense.refill_brand and expense.refill_weight and expense.refill_quantity:
            product, created = Product.objects.get_or_create(
                brand=expense.refill_brand,
                weight=expense.refill_weight,
                defaults={'price': Decimal('0')},
            )

            if expense.refill_state == 'full' and expense.refill_type == 'refill':
                # Empties sent out have come back full — move empty → full
                deduct = min(expense.refill_quantity, product.empty_qty)
                product.empty_qty -= deduct
                product.full_qty  += expense.refill_quantity   # always add full

            elif expense.refill_state == 'full':
                # refill_type == 'new' — fresh cylinders arriving
                product.full_qty += expense.refill_quantity

            else:
                # Empty cylinders arriving / being logged
                product.empty_qty += expense.refill_quantity

            product.save()

            return Response({
                'expense': ExpenseSerializer(expense).data,
                'updated_product': ProductSerializer(product).data,
                'product_created': created,
            }, status=201)

        return Response({
            'expense': ExpenseSerializer(expense).data,
            'updated_product': None,
        }, status=201)
class ExpenseDetailView(APIView):
    def get_object(self, pk):
        return get_object_or_404(Expense, pk=pk)

    def get(self, request, pk):
        if err := require_token(request): return err
        return Response(ExpenseSerializer(self.get_object(pk)).data)

    def patch(self, request, pk):
        if err := require_token(request): return err
        s = ExpenseSerializer(self.get_object(pk), data=request.data, partial=True)
        if s.is_valid():
            s.save()
            return Response(s.data)
        return Response(s.errors, status=400)

    def delete(self, request, pk):
        if err := require_token(request): return err
        self.get_object(pk).delete()
        return Response(status=204)