<?php

namespace App\Services;

use App\Account;
use App\FinancialTransaction;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use RuntimeException;

class MainCashAccount
{
    public function account(bool $lock = false): Account
    {
        $query = Account::where('account_no', 'MAIN-CASH')
            ->where('is_active', true);

        if ($lock) {
            $query->lockForUpdate();
        }

        $account = $query->first();
        if (!$account) {
            throw new RuntimeException(trans('file.Main cash account is not configured'));
        }

        return $account;
    }

    public function deposit(
        float $amount,
        string $category,
        string $referenceType,
        $referenceId = null,
        ?string $referenceNo = null,
        ?string $description = null,
        $transactionDate = null
    ): FinancialTransaction {
        return $this->record('in', $amount, $category, $referenceType, $referenceId, $referenceNo, $description, $transactionDate);
    }

    public function withdraw(
        float $amount,
        string $category,
        string $referenceType,
        $referenceId = null,
        ?string $referenceNo = null,
        ?string $description = null,
        $transactionDate = null
    ): FinancialTransaction {
        return $this->record('out', $amount, $category, $referenceType, $referenceId, $referenceNo, $description, $transactionDate);
    }

    public function reverse(FinancialTransaction $original, ?string $description = null): FinancialTransaction
    {
        if ($original->reversed_at) {
            throw new RuntimeException(trans('file.Transaction has already been reversed'));
        }

        $direction = $original->direction === 'in' ? 'out' : 'in';
        $reversal = $this->record(
            $direction,
            (float) $original->amount,
            'reversal',
            $original->reference_type,
            $original->reference_id,
            $original->reference_no,
            $description ?: trans('file.Transaction reversal'),
            now(),
            $original->id
        );

        $original->reversed_at = now();
        $original->save();

        return $reversal;
    }

    private function record(
        string $direction,
        float $amount,
        string $category,
        string $referenceType,
        $referenceId,
        ?string $referenceNo,
        ?string $description,
        $transactionDate,
        $reversalOfId = null
    ): FinancialTransaction {
        if ($amount <= 0) {
            throw new RuntimeException(trans('file.Amount must be greater than zero'));
        }

        return DB::transaction(function () use (
            $direction,
            $amount,
            $category,
            $referenceType,
            $referenceId,
            $referenceNo,
            $description,
            $transactionDate,
            $reversalOfId
        ) {
            $account = $this->account(true);
            $balance = round((float) $account->total_balance, 2);
            $nextBalance = $direction === 'in'
                ? $balance + round($amount, 2)
                : $balance - round($amount, 2);

            $account->total_balance = $nextBalance;
            $account->save();

            return FinancialTransaction::create([
                'account_id' => $account->id,
                'direction' => $direction,
                'amount' => round($amount, 2),
                'balance_after' => $nextBalance,
                'reference_type' => $referenceType,
                'reference_id' => $referenceId,
                'reference_no' => $referenceNo,
                'category' => $category,
                'description' => $description,
                'transaction_date' => $transactionDate ?: now(),
                'created_by' => Auth::id(),
                'reversal_of_id' => $reversalOfId,
            ]);
        });
    }
}
