<?php

namespace App\Domain\Financial\Services;

use App\Domain\Financial\Events\CashSessionClosed;
use App\Domain\Financial\Models\CashSession;
use App\Domain\Shared\Exceptions\DomainException;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class CashSessionService
{
    public function open(User $user, int $branchId, int $openingBalance, ?string $notes = null): CashSession
    {
        return DB::transaction(function () use ($user, $branchId, $openingBalance, $notes) {
            // Guard: user must not have another open session
            $existingOpen = CashSession::where('user_id', $user->id)
                ->where('status', 'open')
                ->exists();

            if ($existingOpen) {
                throw new DomainException('يوجد وردية مفتوحة بالفعل. يجب إغلاقها أولاً');
            }

            return CashSession::create([
                'academy_id' => $user->academy_id,
                'branch_id' => $branchId,
                'user_id' => $user->id,
                'opening_balance' => $openingBalance,
                'status' => 'open',
                'opened_at' => now(),
                'opening_notes' => $notes,
            ]);
        });
    }

    public function close(CashSession $session, int $closingBalance, ?string $notes = null, ?array $denominations = null, ?User $closedBy = null): CashSession
    {
        return DB::transaction(function () use ($session, $closingBalance, $notes, $denominations, $closedBy) {
            if ($session->status !== 'open') {
                throw new DomainException('هذه الوردية ليست مفتوحة');
            }

            $expectedBalance = $session->opening_balance + $session->total_cash_in - $session->total_cash_out;
            $variance = $closingBalance - $expectedBalance;

            $session->update([
                'closing_balance' => $closingBalance,
                'expected_balance' => $expectedBalance,
                'variance' => $variance,
                'status' => 'closed',
                'closed_at' => now(),
                'closing_notes' => $notes,
                'closed_by' => $closedBy?->id ?? $session->user_id,
                'denominations' => $denominations,
            ]);

            $freshSession = $session->fresh();
            if ($closedBy) {
                CashSessionClosed::dispatch($freshSession, $closedBy);
            }

            return $freshSession;
        });
    }

    public function suspend(CashSession $session, string $reason, User $actor): CashSession
    {
        return DB::transaction(function () use ($session, $reason, $actor) {
            if ($session->status !== 'open') {
                throw new DomainException('هذه الوردية ليست مفتوحة');
            }

            $session->update([
                'status' => 'suspended',
                'closing_notes' => $reason,
                'closed_by' => $actor->id,
            ]);

            return $session->fresh();
        });
    }

    public function recordCashIn(CashSession $session, int $amount): void
    {
        if (!$session->isOpen()) {
            throw new DomainException('لا يمكن تسجيل عملية على وردية مغلقة');
        }

        $session->increment('total_cash_in', $amount);
        $session->increment('transactions_count');
    }

    public function recordCashOut(CashSession $session, int $amount): void
    {
        if (!$session->isOpen()) {
            throw new DomainException('لا يمكن تسجيل عملية على وردية مغلقة');
        }

        $session->increment('total_cash_out', $amount);
        $session->increment('transactions_count');
    }

    public function getOpenSession(User $user): ?CashSession
    {
        return CashSession::where('user_id', $user->id)
            ->where('status', 'open')
            ->first();
    }
}
