<?php
declare(strict_types=1);

namespace App\Modules\Accounting\Models;

use App\Core\Model;

class NegotiableInstrument extends Model
{
    protected static string $table = 'negotiable_instruments';
    protected static array $fillable = [
        'instrument_type', 'direction', 'instrument_number', 'amount', 'currency',
        'issue_date', 'due_date', 'drawer_name', 'drawer_bank',
        'beneficiary_name', 'endorsee_name', 'status', 'current_location',
        'bank_account_id', 'member_id', 'vendor_id', 'payment_id',
        'journal_entry_id', 'notes', 'is_archived', 'created_by',
    ];
    protected static bool $timestamps = true;
    protected static bool $softDelete = true;

    public const STATUS_IN_HAND = 'in_hand';
    public const STATUS_UNDER_COLLECTION = 'under_collection';
    public const STATUS_COLLECTED = 'collected';
    public const STATUS_BOUNCED = 'bounced';
    public const STATUS_ENDORSED = 'endorsed';
    public const STATUS_CANCELLED = 'cancelled';
    public const STATUS_PAID = 'paid';
    public const STATUS_RETURNED = 'returned';

    public static array $statusLabels = [
        'in_hand'          => 'في الخزينة',
        'under_collection' => 'تحت التحصيل',
        'collected'        => 'محصّل',
        'bounced'          => 'مرتد',
        'endorsed'         => 'مظهّر',
        'cancelled'        => 'ملغي',
        'paid'             => 'مدفوع',
        'returned'         => 'مرتجع',
    ];

    public static array $directionLabels = [
        'receivable' => 'أوراق قبض',
        'payable'    => 'أوراق دفع',
    ];

    public static function getReceivables(string $status = ''): array
    {
        $qb = static::query()->where('direction', '=', 'receivable');
        if ($status) {
            $qb->where('status', '=', $status);
        }
        return $qb->orderBy('due_date', 'ASC')->get();
    }

    public static function getPayables(string $status = ''): array
    {
        $qb = static::query()->where('direction', '=', 'payable');
        if ($status) {
            $qb->where('status', '=', $status);
        }
        return $qb->orderBy('due_date', 'ASC')->get();
    }

    public static function getDueWithin(int $days): array
    {
        $future = date('Y-m-d', strtotime("+{$days} days"));
        return static::query()
            ->where('status', '=', 'in_hand')
            ->where('due_date', '<=', $future)
            ->where('due_date', '>=', date('Y-m-d'))
            ->orderBy('due_date', 'ASC')
            ->get();
    }

    public function canTransitionTo(string $newStatus): bool
    {
        $allowed = $this->getAllowedTransitions();
        return in_array($newStatus, $allowed, true);
    }

    public function getAllowedTransitions(): array
    {
        $map = [
            'in_hand'          => ['under_collection', 'endorsed', 'cancelled', 'paid', 'returned'],
            'under_collection' => ['collected', 'bounced', 'returned'],
            'collected'        => [],
            'bounced'          => ['in_hand', 'cancelled'],
            'endorsed'         => ['returned'],
            'cancelled'        => [],
            'paid'             => [],
            'returned'         => ['in_hand'],
        ];

        return $map[$this->status] ?? [];
    }

    public function transitionTo(string $newStatus, ?array $extra = null): bool
    {
        if (!$this->canTransitionTo($newStatus)) {
            return false;
        }

        $data = ['status' => $newStatus];

        if ($newStatus === 'under_collection' || $newStatus === 'collected') {
            $data['current_location'] = 'bank';
        } elseif ($newStatus === 'endorsed') {
            $data['current_location'] = 'endorsed_to';
            if (isset($extra['endorsee_name'])) {
                $data['endorsee_name'] = $extra['endorsee_name'];
            }
        } elseif ($newStatus === 'returned') {
            $data['current_location'] = 'returned_to_drawer';
        } elseif ($newStatus === 'in_hand') {
            $data['current_location'] = 'safe';
        }

        if ($extra) {
            if (isset($extra['bank_account_id'])) {
                $data['bank_account_id'] = $extra['bank_account_id'];
            }
            if (isset($extra['journal_entry_id'])) {
                $data['journal_entry_id'] = $extra['journal_entry_id'];
            }
        }

        $this->update($data);
        return true;
    }
}
