<?php

namespace App\Support;

use App\Models\Account;
use Illuminate\Support\Collection;

class AccountPickerTreeBuilder
{
    /**
     * @param  array<int, string>  $leafTypes  Account types selectable as leaves (e.g. expense, liability, income)
     */
    public static function build(int $companyId, array $leafTypes, bool $activeOnly = true): array
    {
        $allAccounts = Account::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->when($activeOnly, fn ($query) => $query->where('is_active', true))
            ->whereNull('deleted_at')
            ->orderBy('code')
            ->orderBy('name')
            ->get([
                'id', 'parent_id', 'code', 'name', 'type', 'sub_type', 'detail_type',
                'is_group', 'is_bank', 'is_cash', 'sort_order',
            ]);

        $allowedIds = $allAccounts
            ->filter(fn (Account $account) => !$account->is_group && in_array($account->type, $leafTypes, true))
            ->pluck('id')
            ->map(fn ($id) => (int) $id)
            ->flip();

        if ($allowedIds->isEmpty()) {
            return [];
        }

        $neededIds = collect();
        foreach ($allAccounts->whereIn('id', $allowedIds->keys()) as $leaf) {
            $currentId = $leaf->id;
            while ($currentId) {
                $neededIds->put($currentId, true);
                $parentId = $allAccounts->firstWhere('id', $currentId)?->parent_id;
                $currentId = $parentId ? (int) $parentId : null;
            }
        }

        $filtered = $allAccounts
            ->filter(fn (Account $account) => $neededIds->has($account->id))
            ->each(function (Account $account) {
                $account->qb_section = QuickBooksChartOfAccounts::sectionForAccount($account);
                $account->qb_account_type = QuickBooksChartOfAccounts::accountTypeLabel($account);
            });

        $tree = [];

        foreach (QuickBooksChartOfAccounts::sections() as $sectionKey => $sectionMeta) {
            $sectionAccounts = $filtered->filter(
                fn (Account $account) => $account->qb_section === $sectionKey,
            );

            if ($sectionAccounts->isEmpty()) {
                continue;
            }

            $children = static::buildSectionNodes($sectionAccounts, null, $allowedIds);

            if ($children === []) {
                continue;
            }

            $tree[] = [
                'id'         => 'section:' . $sectionKey,
                'name'       => $sectionMeta['label'],
                'code'       => '',
                'type_label' => $sectionMeta['label'],
                'selectable' => false,
                'value'      => null,
                'children'   => $children,
            ];
        }

        return $tree;
    }

    private static function buildSectionNodes(Collection $sectionAccounts, ?int $parentId, Collection $allowedIds): array
    {
        $sectionIds = $sectionAccounts->pluck('id')->flip();
        $nodes = [];

        $siblings = $sectionAccounts
            ->when(
                $parentId === null,
                fn ($accounts) => $accounts->filter(
                    fn (Account $account) => $account->parent_id === null || !$sectionIds->has($account->parent_id),
                ),
                fn ($accounts) => $accounts->where('parent_id', $parentId),
            )
            ->sortBy(fn (Account $account) => sprintf(
                '%06d|%s|%s',
                (int) $account->sort_order,
                $account->code ?? '',
                strtolower($account->name),
            ));

        foreach ($siblings as $account) {
            $children = static::buildSectionNodes($sectionAccounts, $account->id, $allowedIds);
            $isSelectable = !$account->is_group && $allowedIds->has($account->id);

            if ($account->is_group && $children === []) {
                continue;
            }

            if (!$account->is_group && !$isSelectable) {
                continue;
            }

            $nodes[] = [
                'id'         => ($account->is_group ? 'group:' : 'account:') . $account->id,
                'name'       => $account->name,
                'code'       => $account->code ?? '',
                'type_label' => $account->qb_account_type ?? QuickBooksChartOfAccounts::accountTypeLabel($account),
                'selectable' => $isSelectable,
                'value'      => $isSelectable ? (string) $account->id : null,
                'children'   => $children,
            ];
        }

        return $nodes;
    }

    public static function buildForGeneralLedger(int $companyId): array
    {
        return static::build($companyId, ['asset', 'liability', 'equity', 'income', 'expense'], false);
    }
}
