<?php

namespace App\Models\Warehouse\IndirectGoods;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Models\Warehouse\Project;
use App\Models\Warehouse\ProjectDivision;
use App\Models\User;

class IndirectGoodsRequest extends Model
{
    use HasFactory, SoftDeletes;

    protected $table = 'indirect_goods_requests';

    protected $fillable = [
        'request_number',
        'request_date',
        'project_id',
        'division_id',
        'status',
        'requires_approval',
        'requested_by',
        'approved_by',
        'approved_at',
        'issued_by',
        'issued_at',
        'receiver_name',
        'notes',
        'rejection_reason',
        'request_file',
    ];

    protected $casts = [
        'request_date' => 'date',
        'requires_approval' => 'boolean',
        'approved_at' => 'datetime',
        'issued_at' => 'datetime',
    ];

    protected static function boot()
    {
        parent::boot();

        static::creating(function ($request) {
            if (empty($request->request_number)) {
                $request->request_number = static::generateRequestNumber();
            }
        });
    }

    public static function generateRequestNumber(): string
    {
        $prefix = 'IGMR-' . date('Ymd') . '-';
        $last = static::withTrashed()
            ->where('request_number', 'LIKE', $prefix . '%')
            ->orderBy('request_number', 'desc')
            ->first();

        $seq = $last ? ((int) substr($last->request_number, -4) + 1) : 1;

        return $prefix . str_pad($seq, 4, '0', STR_PAD_LEFT);
    }

    /**
     * Approve the request.
     */
    public function approve(int $userId): bool
    {
        if (!in_array($this->status, ['pending_approval'])) {
            return false;
        }

        $this->status = 'approved';
        $this->approved_by = $userId;
        $this->approved_at = now();
        $this->save();

        return true;
    }

    /**
     * Reject the request.
     */
    public function reject(int $userId, string $reason): bool
    {
        if (!in_array($this->status, ['pending_approval', 'draft'])) {
            return false;
        }

        $this->status = 'rejected';
        $this->approved_by = $userId;
        $this->approved_at = now();
        $this->rejection_reason = $reason;
        $this->save();

        return true;
    }

    /**
     * Issue materials - deduct from warehouse inventory, add to project inventory.
     */
    public function issue(int $userId): bool
    {
        if (!in_array($this->status, ['approved', 'partially_issued'])) {
            return false;
        }

        $allIssued = true;

        foreach ($this->items as $requestItem) {
            $remaining = $requestItem->quantity_requested - $requestItem->quantity_released;
            if ($remaining <= 0) {
                continue;
            }

            $issued = $this->issueItem($requestItem, $remaining, $userId);
            if ($issued < $remaining) {
                $allIssued = false;
            }
        }

        $this->issued_by = $userId;
        $this->issued_at = now();
        $this->status = $allIssued ? 'issued' : 'partially_issued';
        $this->save();

        return true;
    }

    /**
     * Issue selected quantities for this request.
     */
    public function issueQuantities(array $quantities, int $userId, array $divisionIds = []): bool
    {
        if (!in_array($this->status, ['approved', 'partially_issued'])) {
            return false;
        }

        foreach ($this->items as $requestItem) {
            $quantity = (float) ($quantities[$requestItem->id] ?? 0);
            if ($quantity <= 0) {
                continue;
            }

            $remaining = max(0, $requestItem->quantity_requested - $requestItem->quantity_released);
            $divisionId = $divisionIds[$requestItem->id] ?? null;
            $this->issueItem($requestItem, min($quantity, $remaining), $userId, $divisionId ? (int) $divisionId : null);
        }

        $this->issued_by = $userId;
        $this->issued_at = now();
        $this->load('items');
        $this->status = $this->items->sum('quantity_balance') <= 0 ? 'issued' : 'partially_issued';
        $this->save();

        return true;
    }

    /**
     * Issue a single item using FIFO by expiry date.
     */
    protected function issueItem(IndirectGoodsRequestItem $requestItem, float $quantity, int $userId, ?int $divisionId = null): float
    {
        $inventoryBatches = IndirectGoodsInventory::where('indirect_good_id', $requestItem->indirect_good_id)
            ->where('quantity_available', '>', 0)
            ->when($divisionId, function ($query) use ($divisionId) {
                $query->where('division_id', $divisionId);
            })
            ->orderBy('expiry_date', 'asc')
            ->orderBy('created_at', 'asc')
            ->get();

        $totalIssued = 0;

        foreach ($inventoryBatches as $batch) {
            if ($totalIssued >= $quantity) {
                break;
            }

            $issueFromBatch = min($batch->quantity_available, $quantity - $totalIssued);

            $quantityBefore = $batch->quantity_available;
            $batch->updateQuantity($issueFromBatch, 0, 'subtract');

            // Add to project inventory
            $projectInv = IndirectGoodsProjectInventory::firstOrNew([
                'project_id' => $this->project_id,
                'indirect_good_id' => $requestItem->indirect_good_id,
                'division_id' => $divisionId ?: $batch->division_id,
                'invoice_number' => $batch->invoice_number,
            ]);

            if ($projectInv->exists) {
                $projectInv->updateQuantity($issueFromBatch, $batch->unit_price, 'add');
            } else {
                $projectInv->batch_number = $batch->batch_number;
                $projectInv->quantity_available = $issueFromBatch;
                $projectInv->quantity_consumed = 0;
                $projectInv->unit_price = $batch->unit_price;
                $projectInv->total_value = $issueFromBatch * $batch->unit_price;
                $projectInv->save();
            }

            // Create stock movement
            IndirectGoodsStockMovement::create([
                'indirect_good_id' => $requestItem->indirect_good_id,
                'movement_type' => 'out',
                'reference_type' => 'request',
                'reference_id' => $this->id,
                'quantity_before' => $quantityBefore,
                'quantity_moved' => $issueFromBatch,
                'quantity_after' => $batch->quantity_available,
                'invoice_number' => $batch->invoice_number,
                'division_id' => $divisionId ?: $batch->division_id ?: $this->division_id,
                'user_id' => $userId,
                'notes' => 'Material request: ' . $this->request_number,
            ]);

            $totalIssued += $issueFromBatch;
        }

        // Update request item
        $requestItem->quantity_released += $totalIssued;
        $requestItem->quantity_balance = $requestItem->quantity_requested - $requestItem->quantity_released;
        $requestItem->unit_price = $inventoryBatches->first()->unit_price ?? 0;
        $requestItem->total_price = $requestItem->quantity_released * $requestItem->unit_price;
        $requestItem->save();

        return $totalIssued;
    }

    // Relationships

    public function items()
    {
        return $this->hasMany(IndirectGoodsRequestItem::class, 'indirect_goods_request_id');
    }

    public function project()
    {
        return $this->belongsTo(Project::class, 'project_id');
    }

    public function division()
    {
        return $this->belongsTo(ProjectDivision::class, 'division_id');
    }

    public function requester()
    {
        return $this->belongsTo(User::class, 'requested_by');
    }

    public function approvedBy()
    {
        return $this->belongsTo(User::class, 'approved_by');
    }

    public function issuedBy()
    {
        return $this->belongsTo(User::class, 'issued_by');
    }

    // Scopes

    public function scopePendingApproval($query)
    {
        return $query->where('status', 'pending_approval');
    }

    public function scopeApproved($query)
    {
        return $query->where('status', 'approved');
    }
}
