<?php

namespace App\Services;

use App\Models\PurchaseRequest;
use App\Models\PurchaseOrder;
use App\Models\PurchaseOrderItem;
use App\Models\Contact;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;

class PurchaseRequestService
{
    /**
     * Recompute PR status based on linked POs
     */
    public function recomputeStatus(PurchaseRequest $purchaseRequest): void
    {
        $totalPrQty = (float) $purchaseRequest->items()->sum('quantity');
        $totalPoQty = (float) PurchaseOrder::where('purchase_request_id', $purchaseRequest->id)
            ->whereNot('status', 'cancelled')
            ->with('items')
            ->get()
            ->sum(fn ($po) => (float) $po->items->sum('quantity'));

        $allClosed = PurchaseOrder::where('purchase_request_id', $purchaseRequest->id)
            ->whereNot('status', 'cancelled')
            ->exists() && !PurchaseOrder::where('purchase_request_id', $purchaseRequest->id)
                ->whereNotIn('status', ['closed','cancelled'])
                ->exists();

        // PR status is now managed by workflow, not by PO quantities
        // Status can only be: draft, submitted, approved, rejected
    }
    /**
     * Convert approved PR to PO with vendor selection
     * Groups PR lines by vendor and creates one PO per vendor
     */
    public function convertToPurchaseOrder(PurchaseRequest $purchaseRequest, array $vendorAssignments): array
    {
        if ($purchaseRequest->status !== 'approved') {
            throw new \Exception('Purchase Request must be approved before converting to PO');
        }

        if (empty($purchaseRequest->items)) {
            throw new \Exception('Purchase Request must have at least one item');
        }

        $createdPos = [];

        DB::transaction(function () use ($purchaseRequest, $vendorAssignments, &$createdPos) {
            // Group items by vendor
            $itemsByVendor = [];

            foreach ($purchaseRequest->items as $item) {
                $vendorId = $vendorAssignments[$item->id] ?? null;
                if (!$vendorId) {
                    throw new \Exception("Vendor not assigned for item: {$item->item->name}");
                }

                if (!isset($itemsByVendor[$vendorId])) {
                    $itemsByVendor[$vendorId] = [];
                }
                $itemsByVendor[$vendorId][] = $item;
            }

            // Create PO for each vendor (one PO per vendor)
            foreach ($itemsByVendor as $vendorId => $items) {
                $vendor = Contact::findOrFail($vendorId);

                // Generate PO number
                $poNumber = $this->generatePONumber();

                $po = PurchaseOrder::create([
                    'po_number' => $poNumber,
                    'purchase_request_id' => $purchaseRequest->id,
                    'vendor_id' => $vendorId,
                    'order_date' => now()->toDateString(),
                    'expected_delivery_date' => $purchaseRequest->required_date,
                    'status' => 'draft',
                    'payment_status' => 'unpaid',
                    'total_amount' => 0,
                ]);

                $totalAmount = 0;

                // Create PO items
                foreach ($items as $prItem) {
                    $poItem = PurchaseOrderItem::create([
                        'purchase_order_id' => $po->id,
                        'purchase_request_item_id' => $prItem->id,
                        'item_id' => $prItem->item_id,
                        'item_variation_id' => $prItem->item_variation_id,
                        'ordered_qty' => $prItem->quantity,
                        'received_qty' => 0,
                        'rate' => 0, // Will be filled when PO is finalized
                        'tax' => 0,
                        'discount' => 0,
                        'total_price' => 0,
                        'uom_id' => $prItem->uom_id,
                    ]);

                    $totalAmount += $poItem->total_price;
                }

                $po->update(['total_amount' => $totalAmount]);
                $createdPos[] = $po;
            }

            // Update PR status to converted
            $purchaseRequest->update(['status' => 'converted_to_po']);
        });

        return $createdPos;
    }

    /**
     * Generate unique PO number
     */
    private function generatePONumber(): string
    {
        $numberService = app(\App\Services\NumberGenerationService::class);
        return $numberService->generatePONumber();
    }



    /**
     * Get available vendors for items
     */
    public function getAvailableVendors(PurchaseRequest $purchaseRequest): array
    {
        $vendors = [];

        foreach ($purchaseRequest->items as $item) {
            $itemVendors = $item->item->suppliers()
                ->where('is_active', true)
                ->where('approval_status', 'approved')
                ->get();

            $vendors[$item->id] = $itemVendors;
        }

        return $vendors;
    }

    /**
     * Validate PR can be converted to PO
     */
    public function validateForConversion(PurchaseRequest $purchaseRequest): array
    {
        $errors = [];

        if ($purchaseRequest->status !== 'approved') {
            $errors[] = 'Purchase Request must be approved before converting to PO';
        }

        if ($purchaseRequest->items->isEmpty()) {
            $errors[] = 'Purchase Request must have at least one item';
        }

        // Check if items have approved suppliers
        foreach ($purchaseRequest->items as $item) {
            $approvedSuppliers = $item->item->approvedSuppliers;
            if ($approvedSuppliers->isEmpty()) {
                $errors[] = "Item '{$item->item->name}' has no approved suppliers";
            }
        }

        return $errors;
    }

    /**
     * Auto-generate POs from approved PR by grouping items by vendor
     * This is a simplified version that assigns items to vendors automatically
     */
    public function autoGeneratePOsFromPR(PurchaseRequest $purchaseRequest): array
    {
        if ($purchaseRequest->status !== 'approved') {
            throw new \Exception('Purchase Request must be approved before generating POs');
        }

        if (empty($purchaseRequest->items)) {
            throw new \Exception('Purchase Request must have at least one item');
        }

        $createdPos = [];

        DB::transaction(function () use ($purchaseRequest, &$createdPos) {
            // Get all vendors
            $vendors = Contact::where('type', 'vendor')->where('is_active', true)->get();

            if ($vendors->isEmpty()) {
                throw new \Exception('No active vendors found');
            }

            // Group items by vendor (simple round-robin assignment)
            $itemsByVendor = [];
            $vendorIndex = 0;
            $vendorCount = $vendors->count();

            foreach ($purchaseRequest->items as $item) {
                $vendorId = $vendors[$vendorIndex]->id;

                if (!isset($itemsByVendor[$vendorId])) {
                    $itemsByVendor[$vendorId] = [];
                }
                $itemsByVendor[$vendorId][] = $item;

                $vendorIndex = ($vendorIndex + 1) % $vendorCount;
            }

            // Create PO for each vendor
            foreach ($itemsByVendor as $vendorId => $items) {
                $vendor = Contact::findOrFail($vendorId);
                $poNumber = $this->generatePONumber();

                $po = PurchaseOrder::create([
                    'po_number' => $poNumber,
                    'purchase_request_id' => $purchaseRequest->id,
                    'vendor_id' => $vendorId,
                    'order_date' => now()->toDateString(),
                    'expected_delivery_date' => $purchaseRequest->required_date,
                    'status' => 'draft',
                    'payment_status' => 'unpaid',
                    'total_amount' => 0,
                ]);

                $total = 0;
                $poItems = [];

                foreach ($items as $prItem) {
                    // Get the preferred supplier price for this item
                    $supplierPrice = $prItem->item->itemSuppliers()
                        ->where('contact_id', $vendorId)
                        ->value('last_price') ?? 0;

                    $poItem = new PurchaseOrderItem([
                        'purchase_order_id' => $po->id,
                        'purchase_request_item_id' => $prItem->id,
                        'item_id' => $prItem->item_id,
                        'item_variation_id' => $prItem->item_variation_id,
                        'ordered_qty' => $prItem->quantity,
                        'received_qty' => 0,
                        'rate' => $supplierPrice,
                        'tax' => 0,
                        'discount' => 0,
                        'total_price' => $prItem->quantity * $supplierPrice,
                        'uom_id' => $prItem->uom_id,
                    ]);

                    $poItems[] = $poItem;
                    $total += $poItem->total_price;
                }

                $po->items()->saveMany($poItems);
                $po->update(['total_amount' => $total]);

                $createdPos[] = $po;
            }
        });

        return $createdPos;
    }

    /**
     * Generate a unique PR number
     *
     * @return string
     */
    public function generatePRNumber(): string
    {
        $numberService = app(\App\Services\NumberGenerationService::class);
        return $numberService->generatePRNumber();
    }

    /**
     * Check if a PR number already exists
     *
     * @param string $prNumber
     * @param int|null $excludeId
     * @return bool
     */
    public function isPRNumberExists(string $prNumber, ?int $excludeId = null): bool
    {
        $query = PurchaseRequest::where('pr_number', $prNumber);

        if ($excludeId) {
            $query->where('id', '!=', $excludeId);
        }

        return $query->exists();
    }

    /**
     * Validate and generate PR number if needed
     *
     * @param string|null $prNumber
     * @param int|null $excludeId
     * @return string
     */
    public function validateAndGeneratePRNumber(?string $prNumber, ?int $excludeId = null): string
    {
        // If no PR number provided or empty, generate one
        if (empty($prNumber)) {
            return $this->generatePRNumber();
        }

        // If PR number provided, check if it's unique
        if ($this->isPRNumberExists($prNumber, $excludeId)) {
            // If not unique, generate a new one
            return $this->generatePRNumber();
        }

        return $prNumber;
    }
}
