<?php

namespace App\Livewire\CashSessions;

use App\Domain\Financial\Models\CashSession;
use App\Domain\Financial\Services\CashSessionService;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('إدارة الوردية')]
class CashSessionManage extends Component
{
    use UsesBranchScope;

    public ?CashSession $currentSession = null;

    /**
     * Drawers this cashier has open in other branches — branch name and when.
     * Display only, and locked: nothing here is read back into a query, but a
     * plain public array is settable from the browser and this one is about
     * where money is sitting.
     *
     * @var array<int, array{branch: string, opened_at: string}>
     */
    #[Locked]
    public array $otherBranchSessions = [];

    // Open form
    public float $opening_balance = 0;
    public string $opening_notes = '';

    // Close form
    public float $closing_balance = 0;
    public string $closing_notes = '';

    public function mount(CashSessionService $service): void
    {
        $this->authorize('cash_sessions.manage');

        $this->loadSession($service);
    }

    /**
     * The drawer for the branch being worked in — asked for by branch rather
     * than left to the global scope, so this screen and the guard in
     * CashSessionService::open() are answering the same question. They were
     * not: this one saw only the current branch, the guard saw every branch,
     * and between them a shift open elsewhere left the desk with a form that
     * refused to submit and no drawer it could close.
     */
    private function loadSession(CashSessionService $service): void
    {
        $branchId = $this->getActiveBranchId();

        $this->currentSession = $service->getOpenSession(auth()->user(), $branchId);

        $this->otherBranchSessions = $branchId
            ? $service->getOpenSessionsInOtherBranches(auth()->user(), $branchId)
                ->map(fn ($s) => [
                    'branch' => $s->branch?->name_ar ?? $s->branch?->name ?? '—',
                    'opened_at' => $s->opened_at?->format('Y-m-d H:i') ?? '—',
                ])
                ->all()
            : [];

        if ($this->currentSession) {
            $expected = ($this->currentSession->opening_balance + $this->currentSession->total_cash_in - $this->currentSession->total_cash_out) / 100;
            $this->closing_balance = $expected;
        }
    }

    public function openSession(CashSessionService $service): void
    {
        $this->validate([
            'opening_balance' => 'required|numeric|min:0',
        ], [
            'opening_balance.required' => 'رصيد الافتتاح مطلوب',
            'opening_balance.min' => 'رصيد الافتتاح يجب ألا يكون سالب',
        ]);

        // A till is opened where the cashier is standing. The branch used to
        // arrive from the form — a plain public property validated with
        // `exists:branches,id`, which accepts every branch in the academy — so a
        // crafted request could open a drawer in another branch and file the
        // whole shift's cash in its books. It now comes from the active branch,
        // and refuses outright when there is no single branch to be in.
        $branchId = $this->getActiveBranchIdOrFail();

        try {
            $amountInPiasters = (int) round($this->opening_balance * 100);
            $this->currentSession = $service->open(auth()->user(), $branchId, $amountInPiasters, $this->opening_notes ?: null);
            $this->opening_balance = 0;
            $this->opening_notes = '';
            session()->flash('success', __('تم فتح الوردية بنجاح'));
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function closeSession(CashSessionService $service): void
    {
        $this->validate([
            'closing_balance' => 'required|numeric|min:0',
        ], [
            'closing_balance.required' => 'رصيد الإغلاق مطلوب',
            'closing_balance.min' => 'رصيد الإغلاق يجب ألا يكون سالب',
        ]);

        try {
            $amountInPiasters = (int) round($this->closing_balance * 100);
            $service->close($this->currentSession, $amountInPiasters, $this->closing_notes ?: null);
            $this->currentSession = null;
            session()->flash('success', __('تم إغلاق الوردية بنجاح'));
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function render()
    {
        return view('livewire.cash-sessions.cash-session-manage', [
            // The branch the drawer will belong to — shown, never chosen. Null
            // only in all-branches mode, where opening a till is refused.
            'branch' => $this->branchContext()->currentBranch(),
        ]);
    }
}
