<?php

namespace App\Http\Controllers\Api;

use App\Events\StockUpdated;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\SalesOrder;
use App\Models\StockBatch;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Dompdf\Dompdf;
use Dompdf\Options;

class SalesController extends Controller
{
    public function payments(Request $request)
    {
        try {
            $query = \App\Models\SalesPayment::query()
                ->select('sales_payments.*')
                ->join('sales_orders', 'sales_payments.sales_order_id', '=', 'sales_orders.id')
                ->join('customers', 'sales_orders.customer_id', '=', 'customers.id')
                ->with(['order', 'order.customer']);

            // Sorting
            $sortBy = $request->input('sort_by', 'payment_date');
            $sortOrder = $request->input('sort_order', 'desc');

            if ($sortBy === 'invoice_no') {
                $query->orderBy('sales_orders.invoice_no', $sortOrder);
            } elseif ($sortBy === 'customer_name') {
                $query->orderBy('customers.name', $sortOrder);
            } else {
                $query->orderBy('sales_payments.' . $sortBy, $sortOrder);
            }

            if ($request->has('search')) {
                $search = $request->search;
                $query->where(function ($q) use ($search) {
                    $q->where('sales_payments.notes', 'like', "%{$search}%")
                        ->orWhere('sales_orders.invoice_no', 'like', "%{$search}%")
                        ->orWhere('sales_orders.customer_name', 'like', "%{$search}%")
                        ->orWhere('customers.phone', 'like', "%{$search}%");
                });
            }

            if ($request->has('from_date') && $request->has('to_date')) {
                $query->whereBetween('sales_payments.payment_date', [
                    $request->from_date,
                    $request->to_date
                ]);
            }

            // Calculate Total before pagination
            $totalAmount = (clone $query)->sum('sales_payments.amount');

            $perPage = $request->input('per_page', 10);
            $payments = $query->paginate($perPage);

            return response()->json([
                'data' => $payments,
                'total_amount' => $totalAmount
            ]);
        } catch (\Exception $e) {
            \Illuminate\Support\Facades\Log::error('Payments Fetch Error: ' . $e->getMessage());
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }

    public function index(Request $request)
    {
        $query = SalesOrder::with([
            'items.batch.brand',
            'items.batch.size',
            'items.batch.subSize',
            'customer',
            'creator',
            'payments'
        ]);

        // Filter by Date Range
        if ($request->has('from_date') && $request->has('to_date')) {
            $query->whereBetween('created_at', [
                $request->from_date . ' 00:00:00',
                $request->to_date . ' 23:59:59'
            ]);
        }

        // Filter by Search (Invoice No, Customer Name, Phone)
        if ($request->has('search')) {
            $search = $request->search;
            $query->where(function ($q) use ($search) {
                $q->where('invoice_no', 'like', "%{$search}%")
                    ->orWhere('customer_name', 'like', "%{$search}%")
                    ->orWhereHas('customer', function ($q) use ($search) {
                        $q->where('phone', 'like', "%{$search}%");
                    });
            });
        }
        // Filter by Status (Payment Status or VOID)
        if ($request->has('status') && $request->status !== 'ALL') {
            if ($request->status === 'VOID') {
                $query->where('status', 'VOID');
            } else {
                $query->where('payment_status', $request->status)
                    ->where('status', '!=', 'VOID');
            }
        }

        // Filter by Brand (via items -> batch)
        if ($request->has('brand_id') && $request->brand_id !== 'ALL') {
            $query->whereHas('items.batch', function ($q) use ($request) {
                $q->where('brand_id', $request->brand_id);
            });
        }

        // Filter by Size (via items -> batch)
        if ($request->has('size_id') && $request->size_id !== 'ALL') {
            $query->whereHas('items.batch', function ($q) use ($request) {
                $q->where('size_id', $request->size_id);
            });
        }

        $perPage = $request->input('per_page', 10);
        return $query->orderBy('id', 'desc')->paginate($perPage);
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'customer_id' => 'nullable|exists:customers,id',
            'customer_name' => 'required|string',
            'customer_phone' => 'required|string',
            'customer_address' => 'nullable|string',
            'customer_gst' => 'nullable|string', // Added GST
            'invoice_date' => 'required|date',
            'items' => 'required|array',
            'items.*.batch_id' => 'required|exists:stock_batches,id',
            'items.*.quantity_pieces' => 'required|numeric|gt:0',
            'items.*.total_weight_kg' => 'nullable|numeric', // Made nullable
            'items.*.price_per_piece' => 'required|numeric|gt:0',
            'gst_rate' => 'required|numeric|min:0',
            'discount_amount' => 'required|numeric|min:0',
            'paid_amount' => 'nullable|numeric|min:0',
        ]);

        try {
            $order = DB::transaction(function () use ($data) {
                // Handle Customer
                if (!empty($data['customer_id'])) {
                    $customer = Customer::findOrFail($data['customer_id']);
                    // Optional: Update GST/Address if provided and empty?
                    if (isset($data['customer_gst']) && empty($customer->gst_no)) {
                        $customer->gst_no = $data['customer_gst'];
                        $customer->save();
                    }
                } else {
                    $customer = Customer::firstOrCreate(
                        ['phone' => $data['customer_phone']],
                        [
                            'name' => $data['customer_name'],
                            'address' => $data['customer_address'] ?? null,
                            'gst_no' => $data['customer_gst'] ?? null,
                            'is_active' => true
                        ]
                    );
                }

                $totalAmount = 0;
                $orderItems = [];

                foreach ($data['items'] as $item) {
                    $batch = StockBatch::lockForUpdate()->find($item['batch_id']);

                    // Validate Stock (by pieces only as requested)
                    if ($batch->available_pieces && isset($item['quantity_pieces'])) {
                        if ($batch->available_pieces < $item['quantity_pieces']) {
                            throw new \Exception("Insufficient pieces for Batch: {$batch->batch_no}");
                        }
                    }

                    // Calculate Deducted Weight (If not provided, try to calculate from pieces)
                    $deductedWeight = $item['total_weight_kg'] ?? 0;
                    if ($deductedWeight <= 0 && $item['quantity_pieces'] > 0 && $batch->weight_per_piece_kg > 0) {
                        $deductedWeight = $item['quantity_pieces'] * $batch->weight_per_piece_kg;
                    }

                    // Deduct Stock
                    StockBatch::withoutAuditing(function () use ($batch, $item, $deductedWeight) {
                        if ($deductedWeight > 0) {
                            $batch->available_weight_kg -= $deductedWeight;
                        }

                        if ($batch->available_pieces && isset($item['quantity_pieces'])) {
                            $batch->available_pieces -= $item['quantity_pieces'];
                        }

                        // Close batch check
                        if (($batch->available_pieces !== null && $batch->available_pieces <= 0) || ($batch->available_weight_kg <= 0.1 && $batch->available_pieces === null)) {
                            $batch->status = 'CLOSED';
                            if ($batch->available_weight_kg < 0)
                                $batch->available_weight_kg = 0;
                        }
                        $batch->save();
                    });

                    // Broadcast Update
                    StockUpdated::dispatch($batch);

                    // Calculate Item Total based on Rate Per Piece
                    $pricePerPiece = $item['price_per_piece'];
                    $quantityPieces = $item['quantity_pieces'];

                    // Item Total = Price/Piece * Pieces
                    $itemTotal = $pricePerPiece * $quantityPieces;
                    $totalAmount += $itemTotal;

                    $orderItems[] = [
                        'batch_id' => $batch->id,
                        'batch_no' => $batch->batch_no,
                        'size_name' => $batch->size ? $batch->size->name : 'Unknown',
                        'sub_size_name' => $batch->subSize ? $batch->subSize->name : null,
                        'quantity_pieces' => $quantityPieces,
                        'total_weight_kg' => $deductedWeight, // Use calculated weight
                        'price_per_kg' => null,
                        'price_per_piece' => $pricePerPiece,
                        'total_price' => $itemTotal,
                    ];
                }

                // Calculate Financials
                $gstRate = $data['gst_rate'] ?? 18.0;
                $discountAmount = $data['discount_amount'] ?? 0;

                $taxAmount = $totalAmount * ($gstRate / 100);
                $netAmount = ($totalAmount + $taxAmount) - $discountAmount;

                // Generate Invoice No: INV-{YY}-{YY+1}-{SEQ} (e.g., INV-25-26-0001)
                $date = \Carbon\Carbon::parse($data['invoice_date']);
                $year = $date->year;
                $month = $date->month;

                // If month is Jan-Mar (1-3), FY started in previous year.
                // e.g. Jan 2026 -> FY 25-26. 
                // If month is Apr-Dec (4-12), FY started in current year.
                // e.g. Apr 2025 -> FY 25-26.

                if ($month >= 4) {
                    $fyStart = $year;
                    $fyEnd = $year + 1;
                } else {
                    $fyStart = $year - 1;
                    $fyEnd = $year;
                }

                $shortStart = substr($fyStart, -2);
                $shortEnd = substr($fyEnd, -2);
                $fyPrefix = "INV-{$shortStart}{$shortEnd}-"; // INV-2526-

                // Custom sequence generation
                $latestOrder = SalesOrder::where('invoice_no', 'like', "{$fyPrefix}%")
                    ->orderBy('id', 'desc')
                    ->first();

                $nextSeq = 1;
                if ($latestOrder) {
                    // Extract sequence number
                    $parts = explode('-', $latestOrder->invoice_no);
                    $seqPart = end($parts); // Get the last part
                    $nextSeq = intval($seqPart) + 1;
                }

                $invoiceNo = $fyPrefix . str_pad($nextSeq, 3, '0', STR_PAD_LEFT);

                // Calculate Payment Status
                $paidAmount = $data['paid_amount'] ?? 0;
                $paymentStatus = 'UNPAID';
                $paidAt = null;

                if ($paidAmount >= $netAmount) {
                    $paymentStatus = 'PAID';
                    $paidAt = now();
                } elseif ($paidAmount > 0) {
                    $paymentStatus = 'PARTIAL';
                    $paidAt = now();
                }

                // Create Order
                $order = SalesOrder::create([
                    'invoice_no' => $invoiceNo,
                    'customer_id' => $customer->id,
                    'invoice_date' => $data['invoice_date'],
                    'customer_name' => $customer->name,
                    'total_amount' => $totalAmount,
                    'gst_rate' => $gstRate,
                    'tax_amount' => $taxAmount,
                    'discount_amount' => $discountAmount,
                    'net_amount' => $netAmount,
                    'paid_amount' => $paidAmount,
                    'payment_status' => $paymentStatus,
                    'paid_at' => $paidAt,
                    'status' => 'COMPLETED'
                ]);

                // Record Initial Payment if any
                if ($paidAmount > 0) {
                    \App\Models\SalesPayment::create([
                        'sales_order_id' => $order->id,
                        'amount' => $paidAmount,
                        'payment_date' => $data['invoice_date'], // Use invoice date as payment date for initial
                        'notes' => 'Initial Payment during Invoice Creation',
                    ]);
                }

                // Create Items
                $order->items()->createMany($orderItems);

                // Log the Invoice Creation Action manually
                \App\Services\AuditService::log('CREATED', 'Sales Invoice', "CREATED Invoice {$order->invoice_no} for {$customer->name}", $order);

                return $order->load('items');
            });

            return response()->json($order, 201);

        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 400);
        }
    }

    public function update(Request $request, $id)
    {
        if (!$request->user() || $request->user()->role !== 'ADMIN') {
            return response()->json(['error' => 'Unauthorized. Only Admins can modify invoices.'], 403);
        }

        $order = SalesOrder::with('items')->findOrFail($id);

        $data = $request->validate([
            'customer_id' => 'nullable|exists:customers,id',
            'customer_name' => 'required|string',
            'customer_phone' => 'required|string',
            'customer_address' => 'nullable|string',
            'customer_gst' => 'nullable|string',
            'invoice_date' => 'required|date',
            'items' => 'required|array',
            'items.*.batch_id' => 'required|exists:stock_batches,id',
            'items.*.quantity_pieces' => 'required|numeric|gt:0',
            'items.*.total_weight_kg' => 'nullable|numeric',
            'items.*.price_per_piece' => 'required|numeric|gt:0',
            'gst_rate' => 'required|numeric|min:0',
            'discount_amount' => 'required|numeric|min:0',
        ]);

        try {
            DB::transaction(function () use ($order, $data) {
                // 1. Restore Stock for Existing Items
                foreach ($order->items as $item) {
                    $batch = \App\Models\StockBatch::lockForUpdate()->find($item->batch_id);
                    if ($batch) {
                        \App\Models\StockBatch::withoutAuditing(function () use ($batch, $item) {
                            if ($item->quantity_pieces > 0) {
                                $batch->available_pieces += $item->quantity_pieces;
                            }
                            if ($item->total_weight_kg > 0) {
                                $batch->available_weight_kg += $item->total_weight_kg;
                            }
                            // Re-open batch if it has stock now
                            if ($batch->status === 'CLOSED' && ($batch->available_pieces > 0 || $batch->available_weight_kg > 0.1)) {
                                $batch->status = 'READY';
                            }
                            $batch->save();
                        });
                        \App\Events\StockUpdated::dispatch($batch);
                    }
                }

                // 2. Clear Existing Items
                $order->items()->delete();

                // 3. Handle Customer Update
                if (!empty($data['customer_id'])) {
                    $customer = \App\Models\Customer::findOrFail($data['customer_id']);
                } else {
                    $customer = \App\Models\Customer::firstOrCreate(
                        ['phone' => $data['customer_phone']],
                        [
                            'name' => $data['customer_name'],
                            'address' => $data['customer_address'] ?? null,
                            'gst_no' => $data['customer_gst'] ?? null,
                            'is_active' => true
                        ]
                    );
                }

                // 4. Process New Items & Deduct Stock
                $totalAmount = 0;
                $orderItems = [];

                foreach ($data['items'] as $item) {
                    $batch = \App\Models\StockBatch::lockForUpdate()->find($item['batch_id']);

                    // Validate Stock
                    if ($batch->available_pieces && isset($item['quantity_pieces'])) {
                        if ($batch->available_pieces < $item['quantity_pieces']) {
                            throw new \Exception("Insufficient pieces for Batch: {$batch->batch_no}");
                        }
                    }

                    // Calculate Deducted Weight logic (Same as Store)
                    $deductedWeight = $item['total_weight_kg'] ?? 0;
                    if ($deductedWeight <= 0 && $item['quantity_pieces'] > 0 && $batch->weight_per_piece_kg > 0) {
                        $deductedWeight = $item['quantity_pieces'] * $batch->weight_per_piece_kg;
                    }

                    // Deduct Stock
                    \App\Models\StockBatch::withoutAuditing(function () use ($batch, $item, $deductedWeight) {
                        if ($deductedWeight > 0) {
                            $batch->available_weight_kg -= $deductedWeight;
                        }

                        if ($batch->available_pieces && isset($item['quantity_pieces'])) {
                            $batch->available_pieces -= $item['quantity_pieces'];
                        }

                        // Close if empty
                        if (($batch->available_pieces !== null && $batch->available_pieces <= 0) || ($batch->available_weight_kg <= 0.1 && $batch->available_pieces === null)) {
                            $batch->status = 'CLOSED';
                            if ($batch->available_weight_kg < 0)
                                $batch->available_weight_kg = 0;
                        }
                        $batch->save();
                    });
                    \App\Events\StockUpdated::dispatch($batch);

                    // Calculations
                    $pricePerPiece = $item['price_per_piece'];
                    $quantityPieces = $item['quantity_pieces'];
                    $itemTotal = $pricePerPiece * $quantityPieces;
                    $totalAmount += $itemTotal;

                    $orderItems[] = [
                        'batch_id' => $batch->id,
                        'batch_no' => $batch->batch_no,
                        'size_name' => $batch->size ? $batch->size->name : 'Unknown',
                        'sub_size_name' => $batch->subSize ? $batch->subSize->name : null,
                        'quantity_pieces' => $quantityPieces,
                        'total_weight_kg' => $deductedWeight,
                        'price_per_piece' => $pricePerPiece,
                        'total_price' => $itemTotal,
                    ];
                }

                // 5. Calculate Financials
                $gstRate = $data['gst_rate'] ?? 18.0;
                $discountAmount = $data['discount_amount'] ?? 0;
                $taxAmount = $totalAmount * ($gstRate / 100);
                $netAmount = ($totalAmount + $taxAmount) - $discountAmount;

                // 6. Update Order Information
                $order->update([
                    'customer_id' => $customer->id,
                    'customer_name' => $customer->name,
                    // 'customer_phone', 'customer_address', 'customer_gst' are not in sales_orders table
                    'invoice_date' => $data['invoice_date'],
                    'total_amount' => $totalAmount,
                    'tax_amount' => $taxAmount,
                    'gst_rate' => $gstRate,
                    'discount_amount' => $discountAmount,
                    'net_amount' => $netAmount,
                ]);

                // 7. Re-Add Items
                $order->items()->createMany($orderItems);

                // 8. Update Payment Status (paid_amount stays same, net_amount changes)
                // Note: If net_amount decreases below paid_amount, we technically owe them money.
                // Logic:
                // If paid >= net => PAID
                // If paid > 0 && paid < net => PARTIAL
                // If paid == 0 => UNPAID
                if ($order->paid_amount >= $order->net_amount) {
                    $order->payment_status = 'PAID';
                } elseif ($order->paid_amount > 0) {
                    $order->payment_status = 'PARTIAL';
                } else {
                    $order->payment_status = 'UNPAID';
                }
                $order->save();
            });

            // Re-fetch fresh order
            return response()->json($order->fresh(['items', 'payments']));
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 400);
        }
    }

    public function updatePayment(Request $request, $id)
    {
        // Enforce ADMIN role for editing payments
        if (!$request->user() || $request->user()->role !== 'ADMIN') {
            return response()->json(['error' => 'Unauthorized. Only Admins can modify payments.'], 403);
        }

        $order = SalesOrder::findOrFail($id);

        $request->validate([
            'amount' => 'required|numeric|min:0.01',
            'payment_date' => 'required|date',
            'notes' => 'nullable|string',
        ]);

        DB::transaction(function () use ($order, $request) {
            // Create Payment Record (SalesPayment tracks individual transactions)
            \App\Models\SalesPayment::create([
                'sales_order_id' => $order->id,
                'amount' => $request->amount,
                'payment_date' => $request->payment_date,
                'notes' => $request->notes,
            ]);

            // Recalculate Total Paid Amount
            $totalPaid = $order->payments()->sum('amount');
            $order->paid_amount = $totalPaid;

            // Update Status
            if ($order->paid_amount >= $order->net_amount) {
                $order->payment_status = 'PAID';
                if (!$order->paid_at) {
                    $order->paid_at = now();
                }
            } elseif ($order->paid_amount > 0) {
                $order->payment_status = 'PARTIAL';
                $order->paid_at = null; // Reset if partial
            } else {
                $order->payment_status = 'UNPAID';
                $order->paid_at = null;
            }

            $order->save();
        });

        return response()->json(['message' => 'Payment recorded successfully', 'order' => $order]);
    }

    public function updatePaymentRecord(Request $request, $id)
    {
        // Enforce ADMIN role
        if (!$request->user() || $request->user()->role !== 'ADMIN') {
            return response()->json(['error' => 'Unauthorized. Only Admins can modify payments.'], 403);
        }

        $payment = \App\Models\SalesPayment::findOrFail($id);
        $order = $payment->order; // Relationship

        $request->validate([
            'amount' => 'required|numeric|min:0.01',
            'payment_date' => 'required|date',
            'notes' => 'nullable|string',
        ]);

        DB::transaction(function () use ($payment, $order, $request) {
            // Update the payment record
            $payment->update([
                'amount' => $request->amount,
                'payment_date' => $request->payment_date,
                'notes' => $request->notes,
            ]);

            // Recalculate Total Paid Amount for the Order
            $totalPaid = $order->payments()->sum('amount');
            $order->paid_amount = $totalPaid;

            // Update Status
            if ($order->paid_amount >= $order->net_amount) {
                $order->payment_status = 'PAID';
                if (!$order->paid_at) {
                    $order->paid_at = now();
                }
            } elseif ($order->paid_amount > 0) {
                $order->payment_status = 'PARTIAL';
                $order->paid_at = null; // Reset if partial
            } else {
                $order->payment_status = 'UNPAID';
                $order->paid_at = null;
            }

            $order->save();
        });

        return response()->json(['message' => 'Payment updated successfully', 'order' => $order]);
    }

    public function void(Request $request, $id)
    {
        if (!$request->user() || $request->user()->role !== 'ADMIN') {
            return response()->json(['error' => 'Unauthorized. Only Admins can void invoices.'], 403);
        }

        $order = SalesOrder::with('items')->findOrFail($id);

        if ($order->status === 'VOID') {
            return response()->json(['error' => 'Invoice is already voided.'], 400);
        }

        DB::transaction(function () use ($order) {
            // 1. Restore Stock
            foreach ($order->items as $item) {
                $batch = StockBatch::find($item->batch_id);
                if ($batch) {
                    $batch->available_pieces += $item->quantity_pieces;
                    $batch->available_weight_kg += $item->total_weight_kg;

                    if ($batch->status === 'CLOSED') {
                        $batch->status = 'ACTIVE';
                    }
                    $batch->save(); // Audit Log will capture this stock update automatically
                }
            }

            // 2. Mark Order as VOID
            $order->status = 'VOID';
            $order->save();

            // 3. Log Action
            \App\Services\AuditService::log('VOID', 'Sales Invoice', "VOIDED Invoice {$order->invoice_no}", $order);
        });

        return response()->json(['message' => 'Invoice voided successfully', 'order' => $order]);
    }

    public function show($id)
    {
        $order = SalesOrder::with(['items.batch', 'customer', 'payments'])->findOrFail($id);
        return response()->json($order);
    }

    public function downloadPdf($id)
    {
        $order = SalesOrder::with(['items.batch', 'customer'])->findOrFail($id);

        // Manual Dompdf instantiation to bypass Service Container/Facade issues
        $options = new Options();
        $options->set('isRemoteEnabled', true);
        $options->set('defaultFont', 'sans-serif');

        $dompdf = new Dompdf($options);


        $bankId = request('bank_id');
        if ($bankId) {
            $bankAccount = \App\Models\BankAccount::find($bankId);
        } else {
            $bankAccount = \App\Models\BankAccount::where('is_default', true)->first();
        }

        $settings = \App\Models\Setting::all()->pluck('value', 'key');

        $html = view('invoices.pdf', compact('order', 'bankAccount', 'settings'))->render();

        $dompdf->loadHtml($html);
        $dompdf->setPaper('A4', 'portrait');
        $dompdf->render();

        return response($dompdf->output())
            ->header('Content-Type', 'application/pdf')
            ->header('Content-Disposition', 'attachment; filename="Invoice_' . $order->invoice_no . '.pdf"');
    }
}
