<?php

namespace App\Http\Controllers\Modules\Purchase;

use App\Http\Controllers\Controller;
use App\Http\Traits\HasRoleViews;
use App\Models\Grn;
use App\Models\GrnProductDetail;
use App\Models\CustomerVendor;
use App\Models\SkuMaster;
use App\Models\SkuMovement;
use App\Models\StockItem;
use App\Models\SalesOrder;
use App\Models\PurchaseOrder;
use App\Models\Purchase;
use App\Services\Purchase\GrnService;
use App\Http\Requests\Purchase\StoreGrnRequest;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * Unified GRN (Goods Receipt Note) Controller
 *
 * Handles GRN management for all roles.
 * Views are automatically resolved based on the logged-in user's role.
 */
class GrnController extends Controller
{
    use HasRoleViews;

    protected GrnService $grnService;

    public function __construct(GrnService $grnService)
    {
        $this->grnService = $grnService;
    }

    /**
     * Display the GRN creation form
     * Alias: grnPage() for backward compatibility
     */
    public function grnPage()
    {
        return $this->create();
    }

    /**
     * Display the GRN list page
     * Alias: grnDetails() for backward compatibility
     */
    public function grnDetails()
    {
        return $this->index();
    }

    /**
     * Display the GRN creation form
     */
    public function create()
    {
        $lastGRN = Grn::orderBy('id', 'desc')->first();
        $nextNumber = $lastGRN ? ((int)explode('/', $lastGRN->grn_no)[2]) + 1 : 1;
        $grnNo = 'UEPL/GRN/' . str_pad($nextNumber, 4, '0', STR_PAD_LEFT);

        $companies = CustomerVendor::all();
        $items = SkuMaster::all();

        return $this->roleView('grn.grnorder', compact('grnNo', 'companies', 'items'));
    }

    /**
     * Display the GRN list page
     */
    public function index()
    {
        $grns = Grn::with('products')->orderBy('id', 'desc')->get();
        $companies = CustomerVendor::all();

        return $this->roleView('grn.grndetails', compact('grns', 'companies'));
    }

    /**
     * Get available orders for GRN (AJAX)
     */
    public function availableOrders(): JsonResponse
    {
        $usedOrderNos = Grn::pluck('order_no')->toArray();

        $salesOrders = SalesOrder::whereNotIn('sales_order_no', $usedOrderNos)
            ->select('sales_order_no as order_no', 'sales_order_date', DB::raw("'sales' as order_type"))
            ->get();

        $purchaseOrders = PurchaseOrder::whereNotIn('purchase_order_no', $usedOrderNos)
            ->select('purchase_order_no as order_no', 'purchase_order_date as order_date', DB::raw("'purchase' as order_type"))
            ->get();

        $orders = $salesOrders->concat($purchaseOrders)->values();

        return response()->json($orders);
    }

    /**
     * Get order details (AJAX)
     */
    public function orderDetails(Request $request): JsonResponse
    {
        $type = $request->query('type');
        $orderNo = $request->query('order_no');

        if ($type === 'sales') {
            $order = SalesOrder::where('sales_order_no', $orderNo)
                ->with('customer')
                ->where('status', 'approved')
                ->first();
        } else {
            $order = PurchaseOrder::where('purchase_order_no', $orderNo)
                ->with('vendor')
                ->where('status', 'approved')
                ->first();
        }

        if (!$order) {
            return response()->json(['error' => 'Order not found'], 404);
        }

        return response()->json($order);
    }

    /**
     * Get available purchase invoices (AJAX)
     */
    public function availablePurchaseInvoices(): JsonResponse
    {
        $usedInvoiceIds = Grn::whereNotNull('purchase_invoice_id')
            ->pluck('purchase_invoice_id')
            ->toArray();

        $invoices = Purchase::with('party')
            ->whereNotIn('id', $usedInvoiceIds)
            ->select('id', 'purchase_no', 'purchase_date', 'vendor_id', 'vendor_invoice_no', 'grand_total')
            ->orderBy('id', 'desc')
            ->get()
            ->map(function ($invoice) {
                return [
                    'id' => $invoice->id,
                    'purchase_no' => $invoice->purchase_no,
                    'purchase_date' => $invoice->purchase_date,
                    'vendor_invoice_no' => $invoice->vendor_invoice_no,
                    'vendor_name' => $invoice->party ? $invoice->party->company : 'Unknown',
                    'grand_total' => $invoice->grand_total,
                ];
            });

        return response()->json($invoices);
    }

    /**
     * Get purchase invoice details (AJAX)
     */
    public function purchaseInvoiceDetails(int $id): JsonResponse
    {
        $invoice = Purchase::with(['party', 'items.item'])->find($id);

        if (!$invoice) {
            return response()->json(['success' => false, 'message' => 'Purchase Invoice not found'], 404);
        }

        return response()->json([
            'success' => true,
            'invoice' => $invoice,
            'items' => $invoice->items->map(function ($item) {
                return [
                    'description' => $item->description ?? ($item->item ? $item->item->item_name : ''),
                    'quantity' => $item->quantity,
                    'unit' => $item->uom ?? 'Nos',
                    'rate' => $item->rate,
                    'amount' => $item->amount,
                ];
            }),
        ]);
    }

    /**
     * Search stock items (AJAX for Select2)
     */
    public function searchStockItems(Request $request): JsonResponse
    {
        $search = $request->get('q', '');

        $items = StockItem::with('category')
            ->when($search, function ($query) use ($search) {
                $query->where('item_name', 'like', "%{$search}%");
            })
            ->limit(20)
            ->get()
            ->map(function ($item) {
                return [
                    'id' => $item->item_name,
                    'text' => $item->item_name . ($item->category ? ' (' . $item->category->name . ')' : ''),
                    'item_name' => $item->item_name,
                    'uom' => $item->uom,
                    'stock_item_id' => $item->id,
                ];
            });

        return response()->json(['results' => $items]);
    }

    /**
     * Store a newly created GRN
     */
    public function store(StoreGrnRequest $request)
    {
        try {
            DB::beginTransaction();

            $grn = Grn::create([
                'grn_no' => $request->grn_no,
                'grn_date' => $request->grn_date,
                'company_id' => $request->company_id,
                'order_no' => $request->order_no,
                'order_type' => $request->order_type,
                'purchase_invoice_id' => $request->purchase_invoice_id,
                'remarks' => $request->remarks,
            ]);

            // Store product details
            if ($request->has('products') && is_array($request->products)) {
                foreach ($request->products as $product) {
                    GrnProductDetail::create([
                        'grn_id' => $grn->id,
                        'item_name' => $product['item_name'],
                        'description' => $product['description'] ?? null,
                        'quantity' => $product['quantity'],
                        'unit' => $product['unit'] ?? 'Nos',
                        'rate' => $product['rate'] ?? 0,
                        'amount' => $product['amount'] ?? 0,
                    ]);
                }
            }

            DB::commit();

            return $this->redirectToRoute('grn.grndetails')
                ->with('success', 'GRN created successfully.');

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('GRN Store Error: ' . $e->getMessage());
            return back()->with('error', 'Something went wrong while creating GRN.');
        }
    }

    /**
     * Display the specified GRN
     */
    public function show(int $id)
    {
        $grn = Grn::with(['products', 'company'])->findOrFail($id);
        return $this->roleView('grn.view', compact('grn'));
    }

    /**
     * Display the edit form
     */
    public function edit(int $id)
    {
        $grn = Grn::with(['products', 'company'])->findOrFail($id);
        $companies = CustomerVendor::all();
        $items = SkuMaster::all();

        return $this->roleView('grn.edit', compact('grn', 'companies', 'items'));
    }

    /**
     * Update the specified GRN
     */
    public function update(Request $request, int $id)
    {
        try {
            $grn = Grn::findOrFail($id);

            $validated = $request->validate([
                'grn_no' => 'required|string|unique:grns,grn_no,' . $id,
                'grn_date' => 'required|date',
                'company_id' => 'required|exists:customer_vendors,id',
            ]);

            DB::beginTransaction();

            $grn->update([
                'grn_no' => $request->grn_no,
                'grn_date' => $request->grn_date,
                'company_id' => $request->company_id,
                'order_no' => $request->order_no,
                'order_type' => $request->order_type,
                'purchase_invoice_id' => $request->purchase_invoice_id,
                'remarks' => $request->remarks,
            ]);

            // Update product details
            $grn->products()->delete();
            if ($request->has('products') && is_array($request->products)) {
                foreach ($request->products as $product) {
                    GrnProductDetail::create([
                        'grn_id' => $grn->id,
                        'item_name' => $product['item_name'],
                        'description' => $product['description'] ?? null,
                        'quantity' => $product['quantity'],
                        'unit' => $product['unit'] ?? 'Nos',
                        'rate' => $product['rate'] ?? 0,
                        'amount' => $product['amount'] ?? 0,
                    ]);
                }
            }

            DB::commit();

            return $this->redirectToRoute('grn.grndetails')
                ->with('success', 'GRN updated successfully.');

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('GRN Update Error: ' . $e->getMessage());
            return back()->with('error', 'Something went wrong while updating GRN.');
        }
    }

    /**
     * Remove the specified GRN
     */
    public function destroy(int $id): JsonResponse
    {
        // Authorization: Only SuperAdmin or Manager can delete
        if (!$this->isSuperAdmin() && !$this->isManager()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized. Only SuperAdmin or Manager can delete GRNs.',
            ], 403);
        }

        try {
            $grn = Grn::findOrFail($id);

            // Prevent deletion of accepted GRNs by non-SuperAdmin
            if ($grn->status === Grn::STATUS_ACCEPTED && !$this->isSuperAdmin()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Cannot delete accepted GRNs. Only SuperAdmin can delete accepted records.',
                ], 403);
            }

            $grn->products()->delete();
            $grn->items()->delete();
            $grn->delete();

            return response()->json(['success' => true, 'message' => 'GRN deleted successfully.']);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => 'Failed to delete GRN.'], 500);
        }
    }

    /**
     * Approve a GRN (SuperAdmin/Manager only)
     */
    public function approve(int $id): JsonResponse
    {
        if (!$this->isSuperAdmin() && !$this->isManager()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized. Only SuperAdmin or Manager can approve GRNs.'
            ], 403);
        }

        try {
            DB::beginTransaction();

            $grn = Grn::with('products')->findOrFail($id);

            // Check if already approved
            if ($grn->status === 'approved') {
                return response()->json([
                    'success' => false,
                    'message' => 'GRN is already approved.'
                ], 422);
            }

            $grn->status = 'approved';
            $grn->approved_by = auth()->id();
            $grn->approved_at = now();
            $grn->save();

            // Update inventory: add stock for each GRN product
            foreach ($grn->products as $product) {
                $sku = SkuMaster::where('item_name', $product->item_name)->first();
                if ($sku) {
                    $sku->addStock(
                        floatval($product->quantity),
                        'GRN-' . $grn->grn_no,
                        'Stock added via GRN approval: ' . $grn->grn_no
                    );
                }
            }

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'GRN approved and inventory updated successfully.'
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('GRN Approval Error: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Failed to approve GRN.'
            ], 500);
        }
    }

    /**
     * Reject a GRN (SuperAdmin/Manager only)
     */
    public function reject(int $id): JsonResponse
    {
        if (!$this->isSuperAdmin() && !$this->isManager()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized. Only SuperAdmin or Manager can reject GRNs.'
            ], 403);
        }

        try {
            $grn = Grn::findOrFail($id);

            // Check if already rejected
            if ($grn->status === 'rejected') {
                return response()->json([
                    'success' => false,
                    'message' => 'GRN is already rejected.'
                ], 422);
            }

            $grn->status = 'rejected';
            $grn->rejected_by = auth()->id();
            $grn->rejected_at = now();
            $grn->save();

            return response()->json([
                'success' => true,
                'message' => 'GRN rejected successfully.'
            ]);
        } catch (\Exception $e) {
            Log::error('GRN Rejection Error: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Failed to reject GRN.'
            ], 500);
        }
    }

    /**
     * Print GRN
     */
    public function print(int $id)
    {
        $grn = Grn::with(['products', 'company'])->findOrFail($id);
        return $this->roleView('grn.print', compact('grn'));
    }
}
