<?php

namespace App\Models\Warehouse;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;

class OutgoingTransaction extends Model
{
    use HasFactory;

    protected $fillable = [
        'transaction_date',
        'project_id',
        'division_id',
        'material_request_number',
        'type_of_goods',
        'requested_by',
        'approved_by',
        'receiver_name',
        'receiver_signature',
        'status',
        'notes',
        'request_file'
    ];

    protected $casts = [
        'transaction_date' => 'date',
        'status' => 'string',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];

    /**
     * Get the project for the transaction
     */
    public function project(): BelongsTo
    {
        return $this->belongsTo(Project::class, 'project_id');
    }

    /**
     * Get the division for the transaction
     */
    public function division(): BelongsTo
    {
        return $this->belongsTo(ProjectDivision::class, 'division_id');
    }

    /**
     * Get the user who requested the materials
     */
    public function requester(): BelongsTo
    {
        return $this->belongsTo(User::class, 'requested_by');
    }

    /**
     * Get the user who approved the request
     */
    public function approver(): BelongsTo
    {
        return $this->belongsTo(User::class, 'approved_by');
    }

    /**
     * Get all items in this transaction
     */
    public function items(): HasMany
    {
        return $this->hasMany(OutgoingItem::class);
    }

    /**
     * Get all fulfillments for this transaction
     */
    public function fulfillments(): HasMany
    {
        return $this->hasMany(OutgoingFulfillment::class);
    }

    /**
     * Get the material delivery note for this transaction
     */
    public function deliveryNote(): HasOne
    {
        return $this->hasOne(MaterialDeliveryNote::class);
    }

    /**
     * Get stock movements created by this transaction
     */
    public function stockMovements()
    {
        return $this->morphMany(StockMovement::class, 'reference');
    }

    /**
     * Scope for pending transactions
     */
    public function scopePending($query)
    {
        return $query->where('status', 'pending');
    }

    /**
     * Scope for successful transactions
     */
    public function scopeSuccessful($query)
    {
        return $query->where('status', 'successful');
    }

    /**
     * Scope for canceled transactions
     */
    public function scopeCanceled($query)
    {
        return $query->where('status', 'canceled');
    }

    /**
     * Scope for converted transactions
     */
    public function scopeConverted($query)
    {
        return $query->whereIn('status', ['converted_to_direct_delivery', 'converted_to_transfer', 'processed_multi_source']);
    }

    /**
     * Scope for multi-source transactions
     */
    public function scopeMultiSource($query)
    {
        return $query->where('status', 'processed_multi_source');
    }

    /**
     * Scope for transactions by project
     */
    public function scopeForProject($query, $projectId)
    {
        return $query->where('project_id', $projectId);
    }

    /**
     * Approve and dispatch the transaction in one step
     */
    public function approveAndDispatch($userId)
    {
        $this->approved_by = $userId;
        $this->status = 'successful';
        $this->save();

        // Update inventory for each item (quantity_released should be set by controller)
        foreach ($this->items as $item) {
            $this->updateInventory($item);
            $this->updateProjectInventory($item);
            $this->createStockMovement($item);
        }
    }

    /**
     * Cancel the transaction
     */
    public function cancel()
    {
        $this->status = 'canceled';
        $this->save();
    }

    /**
     * Update inventory for an item
     */
    protected function updateInventory($outgoingItem)
    {
        $query = Inventory::where('item_id', $outgoingItem->item_id)
            ->where('quantity_available', '>', 0);

        // Check for item-specific division_id first (from outgoing_item)
        // If not available, fall back to transaction's division_id
        $divisionId = $outgoingItem->division_id ?? $this->division_id;

        if ($divisionId) {
            $query->where('division_id', $divisionId);
        }

        $inventories = $query->orderBy('expiry_date', 'asc')->get(); // FIFO by expiry date

        $remainingQuantity = $outgoingItem->quantity_released;
        $totalValue = 0;
        $totalQtyDeducted = 0;

        foreach ($inventories as $inventory) {
            if ($remainingQuantity <= 0) break;

            $quantityToDeduct = min($inventory->quantity_available, $remainingQuantity);

            // Track value from this batch for weighted average calculation
            $totalValue += $quantityToDeduct * $inventory->unit_price;
            $totalQtyDeducted += $quantityToDeduct;

            // Deduct the quantity
            $inventory->quantity_available -= $quantityToDeduct;
            $inventory->save();

            $remainingQuantity -= $quantityToDeduct;

            \Log::info("Inventory deduction", [
                'item_id' => $outgoingItem->item_id,
                'division_id' => $divisionId,
                'inventory_id' => $inventory->id,
                'deducted_quantity' => $quantityToDeduct,
                'batch_unit_price' => $inventory->unit_price,
                'remaining_inventory' => $inventory->quantity_available
            ]);

            // Update division delivery totals when items are issued
            if ($divisionId) {
                \App\Models\Warehouse\DivisionDelivery::recordIssue(
                    $divisionId,
                    $outgoingItem->item_id,
                    $quantityToDeduct
                );
            }
        }

        // Update outgoing item with actual weighted average price from FIFO deduction
        if ($totalQtyDeducted > 0) {
            $actualUnitPrice = $totalValue / $totalQtyDeducted;
            $outgoingItem->unit_price = $actualUnitPrice;
            $outgoingItem->save();
        }

        if ($remainingQuantity > 0) {
            \Log::warning("Insufficient inventory for item", [
                'item_id' => $outgoingItem->item_id,
                'division_id' => $divisionId,
                'requested_quantity' => $outgoingItem->quantity_released,
                'shortage' => $remainingQuantity
            ]);
        }
    }

    /**
     * Update project inventory when materials are dispatched to project
     */
    protected function updateProjectInventory($outgoingItem)
    {
        if (!$this->project_id || $outgoingItem->quantity_released <= 0) {
            return;
        }

        $projectInventory = ProjectInventory::where('project_id', $this->project_id)
            ->where('item_id', $outgoingItem->item_id)
            ->first();

        if ($projectInventory) {
            // Update existing record with weighted average pricing
            $projectInventory->updateQuantity($outgoingItem->quantity_released, 'add', $outgoingItem->unit_price ?: 0);
        } else {
            // Create new record
            ProjectInventory::create([
                'project_id' => $this->project_id,
                'item_id' => $outgoingItem->item_id,
                'quantity_available' => $outgoingItem->quantity_released,
                'allocated_quantity' => 0,
                'unit_price' => $outgoingItem->unit_price ?: 0,
                'total_value' => $outgoingItem->quantity_released * ($outgoingItem->unit_price ?: 0)
            ]);
        }
    }

    /**
     * Create stock movement record
     */
    protected function createStockMovement($outgoingItem)
    {
        StockMovement::create([
            'item_id' => $outgoingItem->item_id,
            'movement_type' => 'out',
            'reference_type' => 'outgoing',
            'reference_id' => $this->id,
            'quantity_before' => 0, // This should be calculated
            'quantity_moved' => $outgoingItem->quantity_released,
            'quantity_after' => 0, // This should be calculated
            'division_id' => $this->division_id,
            'user_id' => $this->requested_by,
            'notes' => "Outgoing to project: {$this->project->project_name}"
        ]);
    }

    /**
     * Check if transaction can be fulfilled
     */
    public function getCanBeFulfilledAttribute()
    {
        foreach ($this->items as $item) {
            $query = Inventory::where('item_id', $item->item_id);

            // Only filter by division if it exists
            if ($this->division_id) {
                $query->where('division_id', $this->division_id);
            }

            $availableQuantity = $query->sum('quantity_available');

            if ($availableQuantity < $item->quantity_requested) {
                return false;
            }
        }

        return true;
    }

    /**
     * Get transaction reference number
     */
    public function getReferenceNumberAttribute()
    {
        return $this->material_request_number ?: 'OUT-' . str_pad($this->id, 6, '0', STR_PAD_LEFT);
    }
}