<?php

namespace App\Models\OPERATION;

use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth;
use App\Models\admin\User;
use App\Models\STOCK\Warehouse;
use App\Models\CRM\Supplier;
use App\Models\CRM\Client;
use App\Models\ACCOUNTING\Accountingentry;
use App\Models\ACCOUNTING\Payment;
use Illuminate\Database\Eloquent\SoftDeletes;
use OwenIt\Auditing\Contracts\Auditable;


class Operation extends Model  implements Auditable
{
    use HasFactory, SoftDeletes, \OwenIt\Auditing\Auditable;

    protected $table = 'operations';

    protected $fillable = [
        'uid',
        'name',
        'status',
        'docId',
        'baseDocId',
        'date',
        'paymentMethod',
        'paymentDue',
        'validateDate',
        'expectedArrival',
        'docType',
        'subject',
        'body',
        'items',
        'discount',
        'totalHt',
        'tax',
        'totalTtc',
        'grandTotal',
        'totalInWord',
        'notes',
        'counterParty',
        'sendTo',
        'conditions',
        'personInCharge',
        'shippingChargeMode',
        'shippingFees',
        'shippingMethod',
        'currency',
        'parentId',
        'invoiceId',
        'warehouseId',
        'fromWarehouseId',
        'toWarehouseId',
        'userId',
        'tableId',
        'roomId',
        'supplierId',
        'clientId',
        'hasSignature',
        'hasStamp',
        'posId'
    ];

    protected $casts = [
        'date' => 'datetime',
        'paymentDue' => 'datetime',
        'paymentMethod' => 'array',
        'validateDate' => 'datetime',
        'expectedArrival' => 'datetime',
        'discount' => 'decimal:2',
        'totalHt' => 'decimal:2',
        'totalTtc' => 'decimal:2',
        'grandTotal' => 'decimal:2',
        'sendTo' => 'array',
        'counterParty' => 'array',
        'items' => 'array',
        'shippingMethod' => 'array',
        'tax' => 'array',
        'currency' => 'array',
        'table' => 'array',
        'room' => 'array',
        'notes' => 'array',

    ];

    /*
    |--------------------------------------------------------------------------
    | JSON Attribute Accessors / Mutators
    |--------------------------------------------------------------------------
    */



    protected static function booted(): void
    {
        static::creating(function ($model) {

            if (empty($model->uid)) {
                $model->uid = (string) Str::ulid();
            }

            if (empty($model->name)) {
                $model->name = $model->docType . '-' . $model->docId;
            }

            if (empty($model->personInCharge) && Auth::check()) {
                $model->personInCharge = Auth::user()->name;
            }

            if (empty($model->userId) && Auth::check()) {
                $model->userId = Auth::id();
            }
        });


        static::updating(function ($model) {
            if (empty($model->personInCharge) && Auth::check()) {
                $model->personInCharge = Auth::user()->name;
            }
        });


        // ✅ status propagation trigger
        static::updated(function ($model) {
            if ($model->wasChanged('status')) {
                $model->propagateStatusToParent();
            }
        });

        static::deleting(function ($operation) {
            if ($operation->isClosed()) {
                throw new \Exception('This operation is closed and cannot be deleted.');
            }

            if ($operation->docType === 'invoicesale' && $operation->payments()->exists()) {
                throw new \Exception('Invoice has payment and cannot be deleted.');
            }
        });

        static::saving(function ($model) {
            if ($model->parentId && $model->parentId == $model->id) {
                throw new \Exception('Operation cannot be parent of itself');
            }
        });
    }


    public function children()
    {
        return $this->hasMany(Operation::class, 'parentId');
    }
    
    public function invoice()
    {
        return $this->belongsTo(Operation::class, 'invoiceId');
    }

    public function parent()
    {
        return $this->belongsTo(Operation::class, 'parentId');
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

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

    public function fromWarehouse()
    {
        return $this->belongsTo(Warehouse::class, 'fromWarehouseId');
    }

    public function toWarehouse()
    {
        return $this->belongsTo(Warehouse::class, 'toWarehouseId');
    }

    public function pos()
    {
        return $this->belongsTo(Pos::class, 'posId');
    }

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

    public function client()
    {
        return $this->belongsTo(Client::class, 'clientId');
    }

    public function accountingentries()
    {
        return $this->hasMany(Accountingentry::class, 'operationId');
    }

    public function payments()
    {
        return $this->hasMany(Payment::class, 'operationId')->with('user');;
    }

    private function supportsQuantityPropagation(string $docType): bool
    {
        return in_array($docType, [
            'purchaseorder',
            'receiptnote',
            'entrynote',
            'estimate',
            'deliverynote',
            'exitnote',
        ]);
    }

    private function childDocTypesFor(string $parentDocType): array
    {
        return match ($parentDocType) {
            'purchaseorder', 'receiptnote', 'entrynote'
            => ['receiptnote', 'entrynote'],

            'estimate', 'deliverynote', 'exitnote'
            => ['deliverynote', 'exitnote'],

            default => [],
        };
    }

    private function getQuantitiesFromChildren(Operation $parent): array
    {
        $received = [];
        foreach ($parent->children as $child) {

            if (in_array($child->status, ['draft', 'cancelled'])) {
                continue;
            }
            if (!in_array($child->docType, ['receiptnote', 'entrynote', 'deliverynote', 'exitnote'])) {
                continue;
            }

            foreach ($child->items ?? [] as $childItem) {
                $itemId = $childItem['item']['id'] ?? null;
                if (!$itemId) continue;

                $qty = (float) ($childItem['qty'] ?? 0);
                $received[$itemId] = ($received[$itemId] ?? 0) + $qty;
            }
        }

        return $received;
    }


    public function propagateStatusToParent(): void
    {
        if (!$this->parentId) {
            return;
        }

        $parent = Operation::with('children')->find($this->parentId);

        if (
            !$parent ||
            empty($parent->items) ||
            !$this->supportsQuantityPropagation($parent->docType)
        ) {
            return;
        }

        $qtyMap = $this->getQuantitiesFromChildren($parent);

        $allDone = true;
        $anyDone = false;

        foreach ($parent->items as $parentItem) {

            $itemId = $parentItem['item']['id'] ?? null;
            if (!$itemId) {
                continue;
            }

            $requiredQty = (float) ($parentItem['qty'] ?? 0);
            $processedQty = (float) ($qtyMap[$itemId] ?? 0);

            if ($processedQty <= 0) {
                $allDone = false;
                continue;
            }

            if ($processedQty < $requiredQty) {
                $anyDone = true;
                $allDone = false;
                continue;
            }

            // processedQty >= requiredQty
            $anyDone = true;
        }

        if ($allDone) {
            $newStatus = $this->mapFullStatus($parent->docType);
        } elseif ($anyDone) {

            $newStatus = $this->mapPartialStatus($parent->docType);
        } else {
            $newStatus = 'confirmed';
        }

        if ($newStatus !== $parent->status) {
            $parent->update(['status' => $newStatus]);

            // propagate up safely
            $parent->propagateStatusToParent();
        }
    }


    /**
     * Map partial status by doc type for parent
     */
    private function mapPartialStatus(string $docType): string
    {
        return match ($docType) {
            'purchaseorder', 'receiptnote', 'entrynote' => 'partiallyReceived',
            'estimate', 'deliverynote', 'exitnote' => 'partiallyDelivered',
            default => 'confirmed',
        };
    }

    /**
     * Map full status by doc type for parent
     */
    private function mapFullStatus(string $docType): string
    {
        return match ($docType) {
            'purchaseorder', 'receiptnote', 'entrynote' => 'received',
            'estimate', 'deliverynote', 'exitnote' => 'delivered',
            default => 'confirmed',
        };
    }

    public function isClosed(): bool
    {
        return in_array($this->status, [
            'received',
            'delivered',
            'invoiced',
            'partiallyReceived',
            'partiallyDelivered',
        ]);
    }
}
