<?php

namespace App\Models\Tenant;

use App\Models\Concerns\BelongsToTenant;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;

class Purchase extends Model
{
    use BelongsToTenant, SoftDeletes;

    public const STATUSES = [
        'draft' => 'Ciornă',
        'ordered' => 'Comandată',
        'partial' => 'Parțial recepționată',
        'received' => 'Recepționată',
        'cancelled' => 'Anulată',
    ];

    protected $fillable = [
        'company_id', 'number', 'supplier_id', 'warehouse_id',
        'order_date', 'expected_at', 'received_at', 'paid_at',
        'status', 'total', 'notes',
    ];

    protected $casts = [
        'order_date' => 'date',
        'expected_at' => 'date',
        'received_at' => 'date',
        'paid_at' => 'date',
        'total' => 'decimal:2',
    ];

    public function supplier(): BelongsTo
    {
        return $this->belongsTo(Supplier::class);
    }

    public function warehouse(): BelongsTo
    {
        return $this->belongsTo(Warehouse::class);
    }

    public function items(): HasMany
    {
        return $this->hasMany(PurchaseItem::class);
    }

    public function recalcTotal(): void
    {
        $this->total = (float) $this->items()->sum('total');
        $this->save();
    }

    public static function generateNumber(int $companyId): string
    {
        $year = date('y');
        $count = static::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->whereYear('created_at', date('Y'))
            ->count();
        return sprintf('P-%s-%04d', $year, $count + 1);
    }

    /**
     * Receive a specific item — qty of buy_price unit cost into target warehouse.
     * Routes through WarehouseService so a batch is created + receipt event written.
     * Also records the supplier price for analytics.
     */
    public function receiveItem(PurchaseItem $item, float $qty, ?Warehouse $warehouse = null): void
    {
        if ($qty <= 0) {
            throw new \InvalidArgumentException('Cantitatea de recepție trebuie să fie pozitivă.');
        }
        $outstanding = (float) $item->qty - (float) $item->qty_received;
        if ($qty > $outstanding + 0.001) {
            throw new \InvalidArgumentException(sprintf(
                'Cantitate prea mare: cerut %.2f, restanță %.2f',
                $qty, $outstanding
            ));
        }

        \Illuminate\Support\Facades\DB::transaction(function () use ($item, $qty, $warehouse) {
            $warehouse ??= $this->warehouse;
            if (! $warehouse) {
                $warehouse = app(\App\Services\Warehouse\WarehouseService::class)
                    ->defaultWarehouse($this->company_id);
            }

            if ($item->part_id) {
                $part = Part::find($item->part_id);
                if ($part) {
                    app(\App\Services\Warehouse\WarehouseService::class)->receive(
                        part: $part,
                        qty: $qty,
                        buyPrice: (float) $item->buy_price,
                        warehouse: $warehouse,
                        supplier: $this->supplier,
                        batchRef: $this->number,
                        ref: $this,
                        notes: "PO #{$this->number}",
                    );

                    if ($this->supplier_id) {
                        SupplierPartPrice::create([
                            'supplier_id' => $this->supplier_id,
                            'part_id' => $part->id,
                            'purchase_id' => $this->id,
                            'price' => (float) $item->buy_price,
                            'currency' => 'MDL',
                            'observed_at' => now(),
                        ]);
                    }
                }
            }

            $item->qty_received = (float) $item->qty_received + $qty;
            if ((float) $item->qty_received >= (float) $item->qty) {
                $item->received = true;
            }
            $item->save();

            $this->recomputeStatus();
        });
    }

    /** Convenience: receive every outstanding item in full. */
    public function receiveAllRemaining(?Warehouse $warehouse = null): void
    {
        foreach ($this->items()->get() as $item) {
            $outstanding = (float) $item->qty - (float) $item->qty_received;
            if ($outstanding > 0) {
                $this->receiveItem($item, $outstanding, $warehouse);
            }
        }
    }

    /** Recalculate status based on item qty_received vs qty. */
    public function recomputeStatus(): void
    {
        if ($this->status === 'cancelled' || $this->status === 'draft') {
            return;
        }
        $items = $this->items()->get();
        if ($items->isEmpty()) return;

        $totals = $items->reduce(function ($carry, $i) {
            $carry['ordered'] += (float) $i->qty;
            $carry['received'] += (float) $i->qty_received;
            return $carry;
        }, ['ordered' => 0.0, 'received' => 0.0]);

        if ($totals['received'] <= 0) {
            $this->status = 'ordered';
        } elseif ($totals['received'] + 0.001 < $totals['ordered']) {
            $this->status = 'partial';
        } else {
            $this->status = 'received';
            if (! $this->received_at) $this->received_at = now();
        }
        $this->save();
    }
}
