<?php

namespace App\Http\Controllers\Company\Accounting;

use App\Exports\ChartOfAccountsExport;
use App\Imports\ChartOfAccountsImport;
use App\Http\Controllers\Controller;
use App\Models\{Account, AuditLog};
use App\Support\{CoaParentPickerBuilder, QuickBooksChartOfAccounts, SalaryComponentAccountQuickAdd};
use App\Helpers\NumberHelper;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Maatwebsite\Excel\Facades\Excel;

class ChartOfAccountsController extends Controller
{
    public function index()
    {
        $this->gate('accounts', 'view');

        $accounts = Account::withoutGlobalScopes()
            ->with('parent')
            ->where('company_id', auth()->user()->company_id)
            ->whereNull('deleted_at')
            ->orderBy('type')
            ->orderBy('code')
            ->orderBy('name')
            ->get();

        $this->attachCurrentBalances($accounts);
        QuickBooksChartOfAccounts::applyDisplayDepth($accounts);

        $tree = $this->buildTree($accounts);
        $sections = QuickBooksChartOfAccounts::sectionsForAccounts($accounts);
        $sectionMeta = QuickBooksChartOfAccounts::sections();
        $parentAccounts = $accounts->where('is_active', true)->values();
        $parentPickerTree = CoaParentPickerBuilder::build(auth()->user()->company_id);
        $parentPickerAccounts = CoaParentPickerBuilder::accountsMap(auth()->user()->company_id);

        $stats = [
            'total'    => $accounts->count(),
            'active'   => $accounts->where('is_active', true)->count(),
            'inactive' => $accounts->where('is_active', false)->count(),
            'postable' => $accounts->where('is_group', false)->count(),
        ];
        $companyId = auth()->user()->company_id;

        return view('accounting.coa.index', compact(
            'accounts',
            'tree',
            'stats',
            'sections',
            'sectionMeta',
            'parentPickerTree',
            'parentPickerAccounts',
            'companyId',
        ));
    }



    public function print()
    {
        $this->gate('accounts', 'view');

        $accounts = Account::withoutGlobalScopes()
            ->with('parent')
            ->where('company_id', auth()->user()->company_id)
            ->whereNull('deleted_at')
            ->orderBy('type')
            ->orderBy('code')
            ->orderBy('name')
            ->get();

        $this->attachCurrentBalances($accounts);
        QuickBooksChartOfAccounts::applyDisplayDepth($accounts);
        $sections = QuickBooksChartOfAccounts::sectionsForAccounts($accounts);
        $stats = [
            'total' => $accounts->count(),
            'active' => $accounts->where('is_active', true)->count(),
            'inactive' => $accounts->where('is_active', false)->count(),
            'postable' => $accounts->where('is_group', false)->count(),
        ];
        $company = auth()->user()->company;

        return view('accounting.coa.print', compact('accounts', 'sections', 'stats', 'company'));
    }

    public function export()
    {
        $this->gate('accounts', 'view');

        $accounts = Account::withoutGlobalScopes()
            ->with('parent')
            ->where('company_id', auth()->user()->company_id)
            ->whereNull('deleted_at')
            ->orderBy('type')
            ->orderBy('code')
            ->orderBy('name')
            ->get();

        $this->attachCurrentBalances($accounts);
        QuickBooksChartOfAccounts::applyDisplayDepth($accounts);

        $filename = 'chart-of-accounts-' . now()->format('Y-m-d-His') . '.xlsx';

        return Excel::download(new ChartOfAccountsExport($accounts), $filename);
    }


    public function import(Request $request)
    {
        $this->gate('accounts', 'create');

        $validated = $request->validate([
            'coa_file' => 'required|file|mimes:xlsx,xls,csv|max:10240',
        ]);

        $import = new ChartOfAccountsImport(auth()->user()->company_id);
        Excel::import($import, $validated['coa_file']);

        $message = "COA import completed: {$import->created} created, {$import->updated} updated, {$import->skipped} skipped.";
        if ($import->balancesSaved > 0) {
            $message .= " {$import->balancesSaved} opening balance(s) saved.";
        }

        AuditLog::record('import', 'accounts', null, null, [
            'created' => $import->created,
            'updated' => $import->updated,
            'skipped' => $import->skipped,
            'errors' => count($import->errors),
        ], 'Chart of Accounts imported from Excel');

        if ($import->errors) {
            return redirect()->route('coa.index')
                ->with('success', $message)
                ->withErrors(['import' => implode(' ', array_slice($import->errors, 0, 5))]);
        }

        return redirect()->route('coa.index')->with('success', $message);
    }

    public function create(Request $request)
    {
        $this->gate('accounts', 'create');
        $companyId = auth()->user()->company_id;
        $parents   = Account::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->whereNull('deleted_at')
            ->where('is_active', true)
            ->orderBy('code')
            ->get();
        $sectionMeta = QuickBooksChartOfAccounts::sections();
        $selectedParent = $request->parent_id ? Account::withoutGlobalScopes()->find($request->parent_id) : null;
        return view('accounting.coa.create', compact('parents', 'sectionMeta', 'selectedParent'));
    }

    public function store(Request $request)
    {
        $this->gate('accounts', 'create');
        $companyId = auth()->user()->company_id;
        $request->merge([
            'parent_id' => $request->filled('parent_id') ? (int) $request->input('parent_id') : null,
        ]);

        $validated = $request->validate([
            'code'            => ['nullable','string','max:30',
                Rule::unique('accounts')->where(fn($q) => $q->where('company_id', $companyId)->whereNull('deleted_at'))],
            'name'            => 'required|string|max:150',
            'account_section' => ['required', Rule::in(array_keys(QuickBooksChartOfAccounts::sections()))],
            'parent_id'       => 'nullable|integer',
            'is_group'        => 'boolean',
            'is_bank'         => 'boolean',
            'is_cash'         => 'boolean',
            'description'     => 'nullable|string|max:500',
        ]);

        $bankCash = $this->resolveBankCashFlags($request, $validated['account_section']);

        $classification = QuickBooksChartOfAccounts::classificationFromForm(
            $validated['account_section'],
            $bankCash['is_bank'],
            $bankCash['is_cash'],
        );

        [$parentId, $depth, $path] = $this->resolveParentHierarchy(
            $validated['parent_id'] ?? null,
            $classification['type'],
            $companyId,
        );
        if ($parentId === false) {
            return back()->withErrors(['parent_id' => 'Invalid parent account.'])->withInput();
        }
        if ($parentId === null && !empty($validated['parent_id'])) {
            return back()->withErrors(['parent_id' => 'Sub-accounts must use the same account type as their parent.'])->withInput();
        }

        $flags = QuickBooksChartOfAccounts::requestFlags(
            $classification['detail_type'] ?? null,
            $bankCash['is_bank'],
            $bankCash['is_cash']
        );

        $account = Account::withoutGlobalScopes()->create([
            'company_id'  => $companyId,
            'parent_id'   => $parentId,
            'code'        => $validated['code'] ?? null,
            'name'        => $validated['name'],
            'type'        => $classification['type'],
            'sub_type'    => $classification['sub_type'],
            'detail_type' => $classification['detail_type'] ?? null,
            'is_group'    => $request->boolean('is_group'),
            'is_bank'     => $flags['is_bank'],
            'is_cash'     => $flags['is_cash'],
            'is_active'   => true,
            'is_system'   => false,
            'description' => $validated['description'] ?? null,
            'depth'       => $depth,
            'path'        => $path,
        ]);

        AuditLog::record('create', 'accounts', (string) $account->id, null,
            ['code' => $account->code, 'name' => $account->name],
            "Account created: {$account->name}");

        return redirect()->route('coa.index')
            ->with('success', "Account '{$account->name}' created successfully.");
    }

    public function storeAjax(Request $request)
    {
        $this->gate('accounts', 'create');

        try {
            $account = $this->createAccountFromRequest($request);
        } catch (\Illuminate\Validation\ValidationException $exception) {
            return response()->json([
                'message' => $exception->validator->errors()->first() ?: 'Validation failed.',
                'errors'  => $exception->errors(),
            ], 422);
        }

        $companyId = auth()->user()->company_id;

        return response()->json([
            'message'              => "Account '{$account->name}' created successfully.",
            'account'              => $this->formatAccountPayload($account),
            'parentPickerTree'     => CoaParentPickerBuilder::build($companyId),
            'parentPickerAccounts' => CoaParentPickerBuilder::accountsMap($companyId),
        ]);
    }

    public function quickStore(Request $request)
    {
        $this->gate('accounts', 'create');

        try {
            $account = $this->createQuickAccountFromRequest($request);
        } catch (\Illuminate\Validation\ValidationException $exception) {
            return response()->json([
                'message' => $exception->validator->errors()->first() ?: 'Validation failed.',
                'errors'  => $exception->errors(),
            ], 422);
        }

        $companyId = auth()->user()->company_id;
        $intent = (string) $request->input('intent', 'ledger');

        $payload = [
            'message'              => "Account '{$account->name}' created successfully.",
            'account'              => $this->formatAccountPayload($account),
            'parentPickerTree'     => CoaParentPickerBuilder::build($companyId),
            'parentPickerAccounts' => CoaParentPickerBuilder::accountsMap($companyId),
        ];

        if ($intent === 'ledger') {
            $payload['trees'] = SalaryComponentAccountQuickAdd::salaryComponentAccountTrees($companyId);
        }

        return response()->json($payload);
    }

    private function createQuickAccountFromRequest(Request $request): Account
    {
        $companyId = auth()->user()->company_id;
        $intent = (string) $request->input('intent', 'ledger');
        $listKey = (string) $request->input('list_key', '');

        $allowedSections = $intent === 'parent'
            ? QuickBooksChartOfAccounts::sections()
            : SalaryComponentAccountQuickAdd::sectionsForListKey($listKey);

        if ($allowedSections === []) {
            throw \Illuminate\Validation\ValidationException::withMessages([
                'list_key' => 'Invalid account list context.',
            ]);
        }

        $request->merge([
            'parent_id' => $request->filled('parent_id') ? (int) $request->input('parent_id') : null,
        ]);

        $rules = [
            'name'            => [
                'required', 'string', 'max:150',
                Rule::unique('accounts')->where(fn ($q) => $q->where('company_id', $companyId)->whereNull('deleted_at')),
            ],
            'code'            => [
                'nullable', 'string', 'max:30',
                Rule::unique('accounts')->where(fn ($q) => $q->where('company_id', $companyId)->whereNull('deleted_at')),
            ],
            'account_section' => ['required', Rule::in(array_keys($allowedSections))],
            'parent_id'       => 'nullable|integer',
            'intent'          => 'nullable|in:ledger,parent',
            'is_group'        => 'nullable|boolean',
        ];

        if ($intent === 'ledger') {
            $rules['list_key'] = ['required', Rule::in(['expense', 'liability', 'deductionCredit'])];
        }

        $validated = $request->validate($rules);
        $classification = QuickBooksChartOfAccounts::classificationFromForm($validated['account_section']);

        if ($intent === 'ledger' && !in_array($classification['type'], SalaryComponentAccountQuickAdd::allowedTypesForListKey($listKey), true)) {
            throw \Illuminate\Validation\ValidationException::withMessages([
                'account_section' => 'Selected account type is not allowed for this ledger field.',
            ]);
        }

        [$parentId, $depth, $path] = $this->resolveParentHierarchy(
            $validated['parent_id'] ?? null,
            $classification['type'],
            $companyId,
        );

        if ($parentId === false) {
            throw \Illuminate\Validation\ValidationException::withMessages([
                'parent_id' => 'Invalid parent account.',
            ]);
        }

        if ($parentId === null && !empty($validated['parent_id'])) {
            throw \Illuminate\Validation\ValidationException::withMessages([
                'parent_id' => 'Sub-accounts must use the same account type as their parent.',
            ]);
        }

        $account = Account::withoutGlobalScopes()->create([
            'company_id'  => $companyId,
            'parent_id'   => $parentId,
            'code'        => $validated['code'] ?? null,
            'name'        => $validated['name'],
            'type'        => $classification['type'],
            'sub_type'    => $classification['sub_type'],
            'detail_type' => $classification['detail_type'] ?? null,
            'is_group'    => $intent === 'parent' ? true : $request->boolean('is_group'),
            'is_bank'     => false,
            'is_cash'     => false,
            'is_active'   => true,
            'is_system'   => false,
            'description' => null,
            'depth'       => $depth,
            'path'        => $path,
        ]);

        AuditLog::record('create', 'accounts', (string) $account->id, null,
            ['code' => $account->code, 'name' => $account->name],
            "Account created: {$account->name}");

        return $account;
    }

    private function createAccountFromRequest(Request $request): Account
    {
        $companyId = auth()->user()->company_id;
        $request->merge([
            'parent_id' => $request->filled('parent_id') ? (int) $request->input('parent_id') : null,
        ]);

        $validated = $request->validate([
            'code'            => [
                'nullable', 'string', 'max:30',
                Rule::unique('accounts')->where(fn ($q) => $q->where('company_id', $companyId)->whereNull('deleted_at')),
            ],
            'name'            => [
                'required', 'string', 'max:150',
                Rule::unique('accounts')->where(fn ($q) => $q->where('company_id', $companyId)->whereNull('deleted_at')),
            ],
            'account_section' => ['required', Rule::in(array_keys(QuickBooksChartOfAccounts::sections()))],
            'parent_id'       => 'nullable|integer',
            'is_group'        => 'boolean',
            'is_bank'         => 'boolean',
            'is_cash'         => 'boolean',
            'description'     => 'nullable|string|max:500',
        ]);

        $bankCash = $this->resolveBankCashFlags($request, $validated['account_section']);

        $classification = QuickBooksChartOfAccounts::classificationFromForm(
            $validated['account_section'],
            $bankCash['is_bank'],
            $bankCash['is_cash'],
        );

        [$parentId, $depth, $path] = $this->resolveParentHierarchy(
            $validated['parent_id'] ?? null,
            $classification['type'],
            $companyId,
        );

        if ($parentId === false) {
            throw \Illuminate\Validation\ValidationException::withMessages([
                'parent_id' => 'Invalid parent account.',
            ]);
        }

        if ($parentId === null && !empty($validated['parent_id'])) {
            throw \Illuminate\Validation\ValidationException::withMessages([
                'parent_id' => 'Sub-accounts must use the same account type as their parent.',
            ]);
        }

        $flags = QuickBooksChartOfAccounts::requestFlags(
            $classification['detail_type'] ?? null,
            $bankCash['is_bank'],
            $bankCash['is_cash']
        );

        $account = Account::withoutGlobalScopes()->create([
            'company_id'  => $companyId,
            'parent_id'   => $parentId,
            'code'        => $validated['code'] ?? null,
            'name'        => $validated['name'],
            'type'        => $classification['type'],
            'sub_type'    => $classification['sub_type'],
            'detail_type' => $classification['detail_type'] ?? null,
            'is_group'    => $request->boolean('is_group'),
            'is_bank'     => $flags['is_bank'],
            'is_cash'     => $flags['is_cash'],
            'is_active'   => true,
            'is_system'   => false,
            'description' => $validated['description'] ?? null,
            'depth'       => $depth,
            'path'        => $path,
        ]);

        AuditLog::record('create', 'accounts', (string) $account->id, null,
            ['code' => $account->code, 'name' => $account->name],
            "Account created: {$account->name}");

        return $account;
    }

    private function formatAccountPayload(Account $account): array
    {
        $code = $account->code ? "[{$account->code}] " : '';
        $typeLabel = QuickBooksChartOfAccounts::accountTypeLabel($account);

        return [
            'id'         => $account->id,
            'code'       => $account->code,
            'name'       => $account->name,
            'type'       => $account->type,
            'type_label' => $typeLabel,
            'label'      => "{$code}{$account->name} ({$typeLabel})",
        ];
    }

    public function edit(Account $account)
    {
        $this->gate('accounts', 'edit');
        $this->sameCompany($account);
        $companyId = auth()->user()->company_id;
        $parents   = Account::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->whereNull('deleted_at')
            ->where('id', '!=', $account->id)
            ->where('is_active', true)
            ->orderBy('code')
            ->get();
        $sectionMeta = QuickBooksChartOfAccounts::sections();
        $accountSection = QuickBooksChartOfAccounts::sectionForAccount($account);
        return view('accounting.coa.edit', compact('account', 'parents', 'sectionMeta', 'accountSection'));
    }

    public function update(Request $request, Account $account)
    {
        $this->gate('accounts', 'edit');
        $this->sameCompany($account);

        if ($account->is_system) {
            return back()->withErrors(['error' => 'System accounts cannot be modified.']);
        }

        $companyId = auth()->user()->company_id;
        $request->merge([
            'parent_id' => $request->filled('parent_id') ? (int) $request->input('parent_id') : null,
        ]);

        $validated = $request->validate([
            'code'            => ['nullable','string','max:30',
                Rule::unique('accounts')
                    ->where(fn($q) => $q->where('company_id', $companyId)->whereNull('deleted_at'))
                    ->ignore($account->id)],
            'name'            => 'required|string|max:150',
            'account_section' => ['required', Rule::in(array_keys(QuickBooksChartOfAccounts::sections()))],
            'parent_id'       => 'nullable|integer',
            'is_group'        => 'boolean',
            'is_bank'         => 'boolean',
            'is_cash'         => 'boolean',
            'is_active'       => 'boolean',
            'description'     => 'nullable|string|max:500',
        ]);

        $bankCash = $this->resolveBankCashFlags($request, $validated['account_section']);

        $classification = QuickBooksChartOfAccounts::classificationFromForm(
            $validated['account_section'],
            $bankCash['is_bank'],
            $bankCash['is_cash'],
        );

        $old = $account->only(['code','name','type','sub_type','detail_type','is_active']);

        if (!empty($validated['parent_id'])) {
            $possibleParent = Account::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->find($validated['parent_id']);
            if ($possibleParent && $this->isDescendant($possibleParent, $account)) {
                return back()->withErrors(['parent_id' => 'An account cannot be moved below one of its sub-accounts.'])->withInput();
            }
        }

        [$parentId, $depth, $path] = $this->resolveParentHierarchy(
            $validated['parent_id'] ?? null,
            $classification['type'],
            $companyId,
            $account,
        );
        if ($parentId === false) {
            return back()->withErrors(['parent_id' => 'Invalid parent account.'])->withInput();
        }
        if ($parentId === null && !empty($validated['parent_id'])) {
            return back()->withErrors(['parent_id' => 'Sub-accounts must use the same account type as their parent.'])->withInput();
        }

        $flags = QuickBooksChartOfAccounts::requestFlags(
            $classification['detail_type'] ?? null,
            $bankCash['is_bank'],
            $bankCash['is_cash']
        );

        $account->update([
            'parent_id'   => $parentId,
            'code'        => $validated['code'] ?? null,
            'name'        => $validated['name'],
            'type'        => $classification['type'],
            'sub_type'    => $classification['sub_type'],
            'detail_type' => $classification['detail_type'] ?? null,
            'is_group'    => $request->boolean('is_group'),
            'is_bank'     => $flags['is_bank'],
            'is_cash'     => $flags['is_cash'],
            'is_active'   => $request->boolean('is_active', true),
            'description' => $validated['description'] ?? null,
            'depth'       => $depth,
            'path'        => $path,
        ]);

        $this->refreshChildrenPaths($account);

        AuditLog::record('update', 'accounts', (string) $account->id, $old,
            $account->only(['code','name','type','sub_type','detail_type','is_active']),
            "Account updated: {$account->name}");

        return redirect()->route('coa.index')
            ->with('success', "Account '{$account->name}' updated successfully.");
    }

    public function destroy(Account $account)
    {
        $this->gate('accounts', 'delete');
        $this->sameCompany($account);

        if ($account->is_system) {
            return back()->withErrors(['error' => 'System accounts cannot be deleted.']);
        }

        $guard = app(\App\Services\MasterDeletionGuard::class);
        if ($message = $guard->blockingMessage($account)) {
            return back()->withErrors(['error' => $message]);
        }

        $name = $account->name;
        $id = $account->id;
        $guard->hardDelete($account);

        AuditLog::record('hard_delete', 'accounts', (string) $id,
            null, null, "Account permanently deleted: {$name}");

        return redirect()->route('coa.index')
            ->with('success', "Account '{$name}' permanently deleted.");
    }

    public function search(Request $request)
    {
        $companyId = auth()->user()->company_id;
        $q         = $request->input('q', '');
        $postable  = $request->boolean('postable', false);

        $query = Account::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->whereNull('deleted_at')
            ->where('is_active', true);

        if ($postable) { $query->where('is_group', false); }
        if ($q) {
            $query->where(fn($qq) =>
                $qq->where('name', 'like', "%{$q}%")->orWhere('code', 'like', "%{$q}%")
            );
        }

        $accounts = $query->orderBy('code')->limit(50)->get(['id','code','name','type','detail_type']);

        return response()->json([
            'results' => $accounts->map(fn($a) => [
                'id'   => $a->id,
                'text' => ($a->code ? "[{$a->code}] " : '') . $a->name,
                'type' => $a->type,
                'detail_type' => $a->detail_type,
            ])
        ]);
    }


    private function attachCurrentBalances($accounts): void
    {
        $companyId = auth()->user()->company_id;
        $balances = DB::table('ledger_entries')
            ->where('company_id', $companyId)
            ->whereIn('account_id', $accounts->pluck('id'))
            ->select('account_id')
            ->selectRaw('COALESCE(SUM(debit),0) - COALESCE(SUM(credit),0) as net_balance')
            ->groupBy('account_id')
            ->pluck('net_balance', 'account_id');

        $profitAndLossSections = ['income', 'other_income', 'cost_of_goods_sold', 'expenses', 'other_expense'];

        $accounts->each(function (Account $account) use ($balances, $profitAndLossSections, $companyId) {
            $section = QuickBooksChartOfAccounts::sectionForAccount($account);
            $account->show_current_balance = !in_array($section, $profitAndLossSections, true);

            if (!$account->show_current_balance) {
                $account->current_balance_display = '—';
                return;
            }

            $net = (float) ($balances[$account->id] ?? 0);
            $normalBalance = in_array($account->type, ['liability', 'equity'], true) ? -$net : $net;
            $suffix = $normalBalance < -0.009 ? (in_array($account->type, ['liability', 'equity'], true) ? ' Dr' : ' Cr') : '';
            $account->current_balance = $normalBalance;
            $account->current_balance_value = abs($normalBalance) < 0.009 ? 0.0 : abs($normalBalance);
            $account->current_balance_suffix = $suffix;
            $account->current_balance_display = NumberHelper::formatAmount($account->current_balance_value, $companyId) . $suffix;
        });
    }

    private function buildTree($accounts, $parentId = null): array
    {
        $tree = [];
        foreach ($accounts->where('parent_id', $parentId) as $account) {
            $account->children_tree = $this->buildTree($accounts, $account->id);
            $tree[] = $account;
        }
        return $tree;
    }

    private function parentPickerTree($accounts, ?int $parentId = null): array
    {
        return $accounts
            ->where('parent_id', $parentId)
            ->sortBy(fn (Account $account) => sprintf('%010s|%s', $account->code ?? '', strtolower($account->name)))
            ->map(fn (Account $account) => [
                'id' => $account->id,
                'code' => $account->code,
                'name' => $account->name,
                'type' => $account->type,
                'children' => $this->parentPickerTree($accounts, $account->id),
            ])
            ->values()
            ->all();
    }

    /**
     * @return array{0: ?int, 1: int, 2: string}|array{0: false, 1: 0, 2: ''}
     */
    private function resolveParentHierarchy(
        ?int $requestedParentId,
        string $type,
        int $companyId,
        ?Account $account = null,
    ): array {
        if (!$requestedParentId) {
            return [null, 0, ''];
        }

        $parent = Account::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->find($requestedParentId);

        if (!$parent || ($account && $parent->id === $account->id)) {
            return [false, 0, ''];
        }

        if ($parent->type !== $type) {
            return [null, 0, ''];
        }

        return [
            $parent->id,
            $parent->depth + 1,
            $parent->path ? $parent->path . '/' . $parent->id : (string) $parent->id,
        ];
    }

    public static function accountTypes(): array
    {
        return QuickBooksChartOfAccounts::accountTypes();
    }

    public static function accountSubTypes(): array
    {
        return QuickBooksChartOfAccounts::subTypes();
    }

    private function resolveBankCashFlags(Request $request, string $accountSection): array
    {
        if ($accountSection !== 'bank') {
            return ['is_bank' => false, 'is_cash' => false];
        }

        $isBank = $request->boolean('is_bank');
        $isCash = $request->boolean('is_cash');

        if ($isBank && $isCash) {
            $isCash = false;
        }

        return ['is_bank' => $isBank, 'is_cash' => $isCash];
    }

    private function isDescendant(Account $possibleDescendant, Account $account): bool
    {
        $path = $possibleDescendant->path ? explode('/', $possibleDescendant->path) : [];

        return in_array((string) $account->id, $path, true);
    }

    private function refreshChildrenPaths(Account $account): void
    {
        $children = Account::withoutGlobalScopes()
            ->where('parent_id', $account->id)
            ->whereNull('deleted_at')
            ->get();

        foreach ($children as $child) {
            $child->depth = $account->depth + 1;
            $child->path = $account->path ? $account->path . '/' . $account->id : (string) $account->id;
            $child->save();
            $this->refreshChildrenPaths($child);
        }
    }

    private function gate(string $module, string $action): void
    {
        if (!auth()->user()->hasPermission($module, $action)) abort(403);
    }

    private function sameCompany(Account $account): void
    {
        if ($account->company_id !== auth()->user()->company_id) abort(403);
    }
}
