<?php

namespace App\Models;

use App\Scopes\DonorPortfolioAccessScope;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Facades\Auth;

class DonorFinancialPortfolio extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'balance',
        'operating_percentage',
        'created_by',
    ];

    protected $casts = [
        'balance' => 'decimal:2',
        'operating_percentage' => 'decimal:2',
    ];

    protected $appends = [
        'remaining_balance',
        'total_allocated',
        'total_operating_value',
    ];

    /**
     * Get the orders that are funded by this portfolio.
     */
    public function orders(): BelongsToMany
    {
        return $this->belongsToMany(Order::class, 'donor_portfolio_order')
            ->withPivot([
                'allocated_amount',
            ])
            ->withTimestamps();
    }

    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class);
    }

    /**
     * Get the users who have access to this portfolio
     */
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'user_donor_financial_portfolio')
            ->withTimestamps();
    }

    /**
     * Get the total allocated amount from this portfolio.
     * Only includes orders that are not rejected.
     */
    protected function totalAllocated(): Attribute
    {
        return Attribute::make(
            get: function () {
                return $this->orders()
                    ->where('orders.status', '!=', Order::STATUS_REJECTED)
                    ->sum('donor_portfolio_order.allocated_amount');
            }
        );
    }

    /**
     * Calculate the remaining balance (balance - total allocated amount).
     */
    protected function remainingBalance(): Attribute
    {
        return Attribute::make(
            get: function () {
                $totalAllocated = $this->total_allocated ?? 0;
                $operatingValue = $this->total_operating_value ?? 0;

                // (balance - totalOperatingValue) - totalAllocated
                return ($this->balance - $operatingValue) - $totalAllocated;
            }
        );
    }

    /**
     * Calculate the total operating value (operating_percentage * balance / 100).
     */
    protected function totalOperatingValue(): Attribute
    {
        return Attribute::make(
            get: function () {
                if (! $this->operating_percentage || ! $this->balance) {
                    return 0;
                }

                return ($this->operating_percentage * $this->balance) / 100;
            }
        );
    }

    /**
     * Check if the portfolio has sufficient balance for a given amount.
     */
    public function hasSufficientBalance(float $amount): bool
    {
        return $this->remaining_balance >= $amount;
    }

    /**
     * Allocate funds to an order.
     */
    public function allocateToOrder(Order $order, float $amount): bool
    {
        if (! $this->hasSufficientBalance($amount)) {
            return false;
        }

        // Check if already allocated to this order
        $existingAllocation = $this->orders()->where('order_id', $order->id)->first();

        if ($existingAllocation) {
            // Update existing allocation
            $this->orders()->updateExistingPivot($order->id, [
                'allocated_amount' => $amount,
                'updated_at' => now(),
            ]);
        } else {
            // Create new allocation
            $this->orders()->attach($order->id, [
                'allocated_amount' => $amount,
                'created_at' => now(),
                'updated_at' => now(),
            ]);
        }

        return true;
    }

    /**
     * Remove allocation from an order.
     */
    public function deallocateFromOrder(Order $order): void
    {
        $this->orders()->detach($order->id);
    }

    /**
     * Get the allocated amount for a specific order.
     */
    public function getAllocatedAmountForOrder(Order $order): float
    {
        $allocation = $this->orders()->where('order_id', $order->id)->first();

        return $allocation ? (float) $allocation->pivot->allocated_amount : 0.0;
    }

    /**
     * Boot the model
     */
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($model) {
            if (Auth::check()) {
                $model->created_by = Auth::id();
            }
        });

        static::created(function ($model) {
            // Automatically link the creator to the portfolio
            if ($model->created_by) {
                $model->users()->syncWithoutDetaching([$model->created_by]);
            }
        });

        // Apply access scope by default
        static::addGlobalScope(new DonorPortfolioAccessScope);
    }

    /**
     * Get portfolios without access scope (for admin operations)
     */
    public static function withoutAccessScope()
    {
        return static::withoutGlobalScope(DonorPortfolioAccessScope::class);
    }
}
