<?php

namespace App\Domain\Inventory\Models;

use App\Domain\Shared\Traits\BelongsToAcademy;
use App\Domain\Shared\Traits\HasUuid;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Domain\Shared\Traits\ScopedThroughBranch;

class ProductInstallmentPlan extends Model
{
    use BelongsToAcademy, HasUuid, ScopedThroughBranch;

    /**
     * The parent whose branch this row inherits. A line item has no
     * branch of its own and must not grow one — a denormalised copy
     * here is a second source of truth that drifts the first time the
     * parent moves.
     */
    protected static string $branchScopeRelation = 'product';

    protected $fillable = [
        'academy_id',
        'product_id',
        'membership_tier',
        'label_ar',
        'installments',
        'frequency',
        'down_payment_pct',
        'installment_amounts',
        'is_active',
        'sort_order',
    ];

    protected function casts(): array
    {
        return [
            'installments' => 'integer',
            'down_payment_pct' => 'integer',
            'installment_amounts' => 'array',
            'is_active' => 'boolean',
            'sort_order' => 'integer',
        ];
    }

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

    public function scopeActive(Builder $query): Builder
    {
        return $query->where('is_active', true);
    }

    public function scopeForTier(Builder $query, string $tier): Builder
    {
        return $query->where(fn ($q) =>
            $q->where('membership_tier', $tier)->orWhere('membership_tier', 'any')
        );
    }

    /**
     * Get the piaster amount for a specific slot (0-based index).
     * If an explicit amount was set for that slot, use it; otherwise auto-calculate.
     */
    public function amountForSlot(int $slotIndex, int $totalPiasters): int
    {
        $explicit = $this->installment_amounts[$slotIndex] ?? null;
        if ($explicit !== null && $explicit > 0) {
            return (int) $explicit;
        }

        // Auto-calculate based on whether this is the first slot (down payment) or regular
        if ($slotIndex === 0) {
            return $this->downPaymentAmount($totalPiasters);
        }
        return $this->regularInstallmentAmount($totalPiasters);
    }

    /** Calculate down payment amount in piasters */
    public function downPaymentAmount(int $totalPiasters): int
    {
        $explicit = $this->installment_amounts[0] ?? null;
        if ($explicit !== null && $explicit > 0) {
            return (int) $explicit;
        }
        if ($this->down_payment_pct <= 0) {
            return (int) ceil($totalPiasters / $this->installments);
        }
        return (int) ceil($totalPiasters * $this->down_payment_pct / 100);
    }

    /** Calculate per-installment amount (after down payment) in piasters */
    public function regularInstallmentAmount(int $totalPiasters): int
    {
        $remaining = $this->installments - 1;
        if ($remaining <= 0) {
            return 0;
        }
        $afterDown = $totalPiasters - $this->downPaymentAmount($totalPiasters);
        return (int) ceil($afterDown / $remaining);
    }

    /**
     * Build the full schedule of amounts for all slots.
     * Uses explicit amounts where set, auto-calculates the rest.
     *
     * The schedule sums to exactly $totalPiasters. It used to round each auto
     * slot up, so three instalments of a 8,000.00 card came to 8,000.01 and the
     * plan asked for a piaster the invoice never billed — a plan that can never
     * reach `completed`. Piasters are split the way every other split in this
     * system is: floor each share, and the last auto slot carries the
     * remainder.
     */
    public function buildSchedule(int $totalPiasters): array
    {
        $explicitSlots = [];
        $explicitSum = 0;

        for ($i = 0; $i < $this->installments; $i++) {
            $explicit = $this->installment_amounts[$i] ?? null;
            if ($explicit !== null && $explicit > 0) {
                $explicitSum += (int) $explicit;
                $explicitSlots[$i] = (int) $explicit;
            }
        }

        $autoIndexes = array_values(array_diff(range(0, $this->installments - 1), array_keys($explicitSlots)));
        $autoCount = count($autoIndexes);
        $remainingForAuto = max(0, $totalPiasters - $explicitSum);

        $schedule = $explicitSlots;

        if ($autoCount > 0) {
            $share = intdiv($remainingForAuto, $autoCount);
            foreach ($autoIndexes as $position => $index) {
                $schedule[$index] = $position === $autoCount - 1
                    ? $remainingForAuto - ($share * ($autoCount - 1))
                    : $share;
            }
        }

        ksort($schedule);

        return $schedule;
    }

    public function frequencyLabel(): string
    {
        return match ($this->frequency) {
            'weekly' => 'أسبوعي',
            'biweekly' => 'نصف شهري',
            'monthly' => 'شهري',
            'quarterly' => 'ربع سنوي',
            default => $this->frequency,
        };
    }

    /**
     * Which tier the plan is offered to, in the branch's own words.
     *
     * The table has no branch of its own — a plan hangs off a product — so
     * this follows the branch being worked in. That is the branch whose till
     * is open, which is the only place a plan is ever offered.
     */
    public function tierLabel(): string
    {
        return match ($this->membership_tier) {
            'member' => term('members'),
            'non_member' => term('non_members'),
            default => 'الجميع',
        };
    }
}
