from django.shortcuts import render, redirect, get_object_or_404
from .models import *
from django.utils import timezone
from django.db.models import Q
from django.contrib import messages


# ✅ Purchase Number Generator
def generate_purchase_number():
    last_purchase = PurchaseReceiving.objects.all().order_by('-id').first()
    if last_purchase and last_purchase.purchase_number:
        try:
            last_num = int(last_purchase.purchase_number)
            new_num = last_num + 1
        except ValueError:
            new_num = 1
    else:
        new_num = 1
    return str(new_num).zfill(5)  # 00001, 00002, 00003...


from django.db.models import Q

# ✅ Purchase Receiving View
def purchase_receiving(request):
    if not request.session.get('user_id'):
        return redirect('login')

    # Handle Delete
    delete_id = request.GET.get('delete_id')
    if delete_id:
        purchase = PurchaseReceiving.objects.filter(id=delete_id).first()
        if purchase:
            purchase.delete()
            messages.success(request, "Purchase record deleted!")
        return redirect('purchase_receiving')

    # Handle POST (Save / Update)
    if request.method == "POST":
        record_id      = request.POST.get('record_id')
        vendor_name    = request.POST.get('vendor_name')
        date           = request.POST.get('date')
        vendor_rate    = request.POST.get('vendor_rate') or 0
        market_rate    = request.POST.get('market_rate') or 0
        weight         = request.POST.get('weight') or 0
        vehicle_number = request.POST.get('vehicle_number')
        status         = request.POST.get('status') or 'Active'

        weight = float(weight)
        vendor_rate = float(vendor_rate)
        amount = weight * vendor_rate

        if record_id:
            # ✅ UPDATE
            purchase = PurchaseReceiving.objects.filter(id=record_id).first()
            if purchase:
                old_weight = float(purchase.weight or 0)
                old_remaining = float(purchase.remaining_weight or 0)

                # Remaining weight adjust karo
                weight_diff = weight - old_weight
                new_remaining = old_remaining + weight_diff

                purchase.vendor_name    = vendor_name
                purchase.date           = date
                purchase.vendor_rate    = vendor_rate
                purchase.market_rate    = market_rate
                purchase.weight         = weight
                purchase.vehicle_number = vehicle_number
                purchase.status         = status
                purchase.remaining_weight = new_remaining
                purchase.amount         = str(amount)
                purchase.save()
                messages.success(request, "Purchase record updated!")
        else:
            # ✅ NEW - Auto Generate Purchase Number
            purchase_number = generate_purchase_number()

            PurchaseReceiving.objects.create(
                purchase_number  = purchase_number,
                vendor_name      = vendor_name,
                date             = date,
                vendor_rate      = vendor_rate,
                market_rate      = market_rate,
                weight           = weight,
                vehicle_number   = vehicle_number,
                status           = status,
                remaining_weight = weight,  # Initially remaining = total weight
                amount           = str(amount),
                payed_amount     = '0',

            )
            messages.success(request, f"Purchase saved! Number: {purchase_number}")

        return redirect('purchase_receiving')

    # Edit Object
    edit_obj = None
    edit_id = request.GET.get('edit_id')
    if edit_id:
        edit_obj = PurchaseReceiving.objects.filter(id=edit_id).first()

    # Vendor List (Vendor + Trader dono)
    vendor_list = Parties.objects.filter(
        Q(party_type__iexact='vendor') | Q(party_type__iexact='trader')
    ).order_by('company_name')

    # History
    history = PurchaseReceiving.objects.all().order_by('-id')

    # Next Purchase Number Preview
    next_purchase_number = generate_purchase_number()

    # ✅ Sabse last enter hua Broiler Rate (Market Rate auto-fill ke liye)
    latest_broiler = BroilerRate.objects.order_by('-date', '-id').first()
    latest_market_rate = latest_broiler.rate if latest_broiler else ''

    context = {
        'edit_obj':              edit_obj,
        'vendor_list':           vendor_list,
        'history':               history,
        'current_date':          timezone.now().date().strftime('%Y-%m-%d'),
        'next_purchase_number':  next_purchase_number,
        'latest_market_rate':    latest_market_rate,
    }
    return render(request, 'Inventory_managment/purchase_receiving.html', context)



from decimal import Decimal, InvalidOperation
from django.contrib import messages
from django.db import transaction
from django.db.models import Q
from django.shortcuts import render, redirect
from django.utils import timezone

from .models import (
    PurchaseReceiving,
    SellsInvoice,
    Parties,
    Mortality,
)


# ============================================================
# SAFE DECIMAL
# ============================================================

def sell_decimal(value):
    try:
        if value is None:
            return Decimal('0')

        value = str(value).strip()

        if not value:
            return Decimal('0')

        return Decimal(value)

    except (InvalidOperation, ValueError, TypeError):
        return Decimal('0')


# ============================================================
# CLEAN NUMBER
# 100.00 -> 100
# -50.00 -> -50
# 10.50 -> 10.5
# ============================================================

def sell_number(value):

    value = sell_decimal(value)

    if value == value.to_integral():
        return str(value.quantize(Decimal('1')))

    return format(value.normalize(), 'f')


# ============================================================
# SELLS VIEW
# ============================================================

def sells(request):

    # --------------------------------------------------------
    # LOGIN
    # --------------------------------------------------------

    if not request.session.get('user_id'):
        return redirect('login')


    # ========================================================
    # CURRENT PURCHASE
    #
    # IMPORTANT:
    # Latest PurchaseReceiving record hamesha current hoga.
    #
    # Remaining:
    # 100  -> show
    # 0    -> show
    # -50  -> show
    # -500 -> show
    #
    # Jab NEW PurchaseReceiving row insert hogi,
    # woh automatically current purchase ban jayegi.
    # ========================================================

    active_purchase = (
        PurchaseReceiving.objects
        .order_by('-id')
        .first()
    )


    # ========================================================
    # DELETE SELL
    #
    # Sale delete ho to weight usi purchase number ke
    # remaining_weight mein wapis add hoga.
    # ========================================================

    delete_id = request.GET.get('delete_id')

    if delete_id:

        try:

            with transaction.atomic():

                sell_obj = (
                    SellsInvoice.objects
                    .select_for_update()
                    .filter(id=delete_id)
                    .first()
                )

                if not sell_obj:
                    messages.error(
                        request,
                        'Sell record not found.'
                    )
                    return redirect('sells')


                purchase_number = (
                    sell_obj.purchase_number or ''
                ).strip()


                purchase_obj = (
                    PurchaseReceiving.objects
                    .select_for_update()
                    .filter(
                        purchase_number=purchase_number
                    )
                    .first()
                )


                if purchase_obj:

                    current_remaining = sell_decimal(
                        purchase_obj.remaining_weight
                    )

                    deleted_weight = sell_decimal(
                        sell_obj.weight
                    )

                    restored_remaining = (
                        current_remaining
                        +
                        deleted_weight
                    )

                    purchase_obj.remaining_weight = (
                        sell_number(restored_remaining)
                    )

                    purchase_obj.save(
                        update_fields=[
                            'remaining_weight'
                        ]
                    )


                sell_obj.delete()


            messages.success(
                request,
                'Sell deleted and stock restored successfully.'
            )


        except Exception as e:

            messages.error(
                request,
                f'Delete Error: {str(e)}'
            )


        return redirect('sells')


    # ========================================================
    # MORTALITY
    # Only positive remaining stock mortality mein jayega.
    # ========================================================

    if (
        request.method == 'POST'
        and
        request.POST.get('action') == 'mortality'
    ):

        if not active_purchase:

            messages.error(
                request,
                'No purchase record found.'
            )

            return redirect('sells')


        try:

            with transaction.atomic():

                purchase_obj = (
                    PurchaseReceiving.objects
                    .select_for_update()
                    .filter(id=active_purchase.id)
                    .first()
                )


                if not purchase_obj:
                    raise ValueError(
                        'Purchase record not found.'
                    )


                remaining = sell_decimal(
                    purchase_obj.remaining_weight
                )


                if remaining <= 0:
                    raise ValueError(
                        'Mortality can only be entered '
                        'when remaining stock is greater than 0.'
                    )


                rate = sell_decimal(
                    purchase_obj.vendor_rate
                )

                amount = remaining * rate


                Mortality.objects.create(

                    purchase_no=(
                        purchase_obj.purchase_number
                        or ''
                    ),

                    vehicle_number=(
                        purchase_obj.vehicle_number
                        or ''
                    ),

                    weight=sell_number(
                        remaining
                    ),

                    rate=sell_number(
                        rate
                    ),

                    amount=sell_number(
                        amount
                    ),

                    entry_date=timezone.localdate()
                )


                purchase_obj.remaining_weight = '0'

                purchase_obj.save(
                    update_fields=[
                        'remaining_weight'
                    ]
                )


            messages.success(
                request,
                'Remaining stock moved to mortality.'
            )


        except Exception as e:

            messages.error(
                request,
                str(e)
            )


        return redirect('sells')


    # ========================================================
    # NEW SELL
    # ========================================================

    if request.method == 'POST':

        customer_name = (
            request.POST.get('customer_name')
            or ''
        ).strip()

        vehicle_no = (
            request.POST.get('vehicle_no')
            or ''
        ).strip()

        sell_weight = sell_decimal(
            request.POST.get('weight')
        )

        sell_rate = sell_decimal(
            request.POST.get('rate')
        )


        # ----------------------------------------------------
        # BASIC VALIDATION
        # ----------------------------------------------------

        if not customer_name:

            messages.error(
                request,
                'Please select Customer / Trader.'
            )

            return redirect('sells')


        if sell_weight <= 0:

            messages.error(
                request,
                'Sell weight must be greater than 0.'
            )

            return redirect('sells')


        if sell_rate <= 0:

            messages.error(
                request,
                'Rate must be greater than 0.'
            )

            return redirect('sells')


        if not active_purchase:

            messages.error(
                request,
                'Please create a Purchase Receiving record first.'
            )

            return redirect('sells')


        try:

            with transaction.atomic():

                # =================================================
                # LOCK SAME PURCHASE
                # =================================================

                purchase_obj = (
                    PurchaseReceiving.objects
                    .select_for_update()
                    .filter(
                        id=active_purchase.id
                    )
                    .first()
                )


                if not purchase_obj:
                    raise ValueError(
                        'Purchase record not found.'
                    )


                # =================================================
                # IMPORTANT CONCURRENCY CHECK
                #
                # Agar form khula tha aur beech mein NEW purchase
                # aa gayi to new sell old purchase par nahi lagegi.
                # User refresh karke latest purchase dekhega.
                # =================================================

                latest_purchase = (
                    PurchaseReceiving.objects
                    .order_by('-id')
                    .first()
                )


                if (
                    latest_purchase
                    and
                    latest_purchase.id != purchase_obj.id
                ):

                    raise ValueError(
                        'A new purchase has been received. '
                        'Page refresh karke sell dobara enter karein.'
                    )


                purchase_number = (
                    purchase_obj.purchase_number or ''
                ).strip()


                if not purchase_number:

                    raise ValueError(
                        'Current purchase number is missing.'
                    )


                # =================================================
                # CURRENT REMAINING
                #
                # Positive/zero/negative sab allowed.
                # =================================================

                old_remaining = sell_decimal(
                    purchase_obj.remaining_weight
                )


                # =================================================
                # NO MAXIMUM STOCK CHECK
                #
                # Examples:
                #
                # Remaining = 100, Sell = 150 => -50
                # Remaining =   0, Sell =  50 => -50
                # Remaining = -50, Sell =  30 => -80
                # =================================================

                new_remaining = (
                    old_remaining
                    -
                    sell_weight
                )


                # =================================================
                # SELL AMOUNT
                # =================================================

                calculated_amount = (
                    sell_weight
                    *
                    sell_rate
                )


                # =================================================
                # SELL NUMBER
                # =================================================

                last_sell = (
                    SellsInvoice.objects
                    .order_by('-id')
                    .first()
                )


                next_number = 1

                if (
                    last_sell
                    and
                    last_sell.sells_no
                ):

                    try:

                        next_number = (
                            int(last_sell.sells_no)
                            +
                            1
                        )

                    except (ValueError, TypeError):

                        next_number = (
                            (last_sell.id or 0)
                            +
                            1
                        )


                sells_no = str(
                    next_number
                ).zfill(4)


                # =================================================
                # 1. NEW ROW IN SELLS INVOICE
                # =================================================

                SellsInvoice.objects.create(

                    sells_no=sells_no,

                    purchase_number=purchase_number,

                    customer_name=customer_name,

                    weight=sell_number(
                        sell_weight
                    ),

                    rate=sell_number(
                        sell_rate
                    ),

                    amount=sell_number(
                        calculated_amount
                    ),

                    received_amount='0',

                    remaining_amount=sell_number(
                        calculated_amount
                    ),

                    vehicle_no=(
                        vehicle_no
                        or
                        purchase_obj.vehicle_number
                        or
                        ''
                    ),

                    entry_date=timezone.now()
                )


                # =================================================
                # 2. PURCHASE_RECEIVING UPDATE
                #
                # remaining_weight keeps going negative.
                # =================================================

                purchase_obj.remaining_weight = (
                    sell_number(
                        new_remaining
                    )
                )


                # NOTE:
                # Status ko Completed nahi kar rahe.
                # Current purchase selection status par dependent
                # nahi hai. Latest purchase id current purchase hai.

                purchase_obj.save(
                    update_fields=[
                        'remaining_weight'
                    ]
                )


            messages.success(
                request,
                f'Sell #{sells_no} saved | '
                f'Purchase #{purchase_number} | '
                f'Old Stock: {sell_number(old_remaining)} kg | '
                f'Sold: {sell_number(sell_weight)} kg | '
                f'New Stock: {sell_number(new_remaining)} kg'
            )


        except Exception as e:

            messages.error(
                request,
                f'Error: {str(e)}'
            )


        return redirect('sells')


    # ========================================================
    # CUSTOMER / TRADER
    # ========================================================

    customer_list = (
        Parties.objects
        .filter(
            Q(party_type__iexact='customer')
            |
            Q(party_type__iexact='trader')
        )
        .order_by('company_name')
    )


    # ========================================================
    # HISTORY
    # ========================================================

    history = (
        SellsInvoice.objects
        .all()
        .order_by('-id')
    )


    for item in history:

        amount = sell_decimal(
            item.amount
        )

        if not item.amount:

            amount = (
                sell_decimal(item.weight)
                *
                sell_decimal(item.rate)
            )

        item.total_display = sell_number(
            amount
        )


    # ========================================================
    # CURRENT PURCHASE DISPLAY
    # ========================================================

    if active_purchase:

        current_remaining = sell_decimal(
            active_purchase.remaining_weight
        )

        available_stock = sell_number(
            current_remaining
        )

        auto_vehicle = (
            active_purchase.vehicle_number
            or ''
        )

        stock_negative = (
            current_remaining < 0
        )

        stock_zero = (
            current_remaining == 0
        )


    else:

        available_stock = '0'

        auto_vehicle = ''

        stock_negative = False

        stock_zero = True


    # ========================================================
    # CONTEXT
    # ========================================================

    context = {

        'active_purchase':
            active_purchase,

        'customer_list':
            customer_list,

        'history':
            history,

        'available_stock':
            available_stock,

        'auto_vehicle':
            auto_vehicle,

        'stock_negative':
            stock_negative,

        'stock_zero':
            stock_zero,

        'current_date':
            timezone.localdate().strftime(
                '%Y-%m-%d'
            ),

        'current_date_display':
            timezone.localdate().strftime(
                '%d-%m-%Y'
            ),
    }


    return render(
        request,
        'Inventory_managment/sells.html',
        context
    )
















