<?php

namespace App\Http\Controllers\Company\Masters;

use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use App\Models\ItemCategory;
use App\Models\Setting;
use App\Models\Warehouse;
use App\Services\OpeningBalanceService;
use App\Support\QuickBooksChartOfAccounts;
use Illuminate\Http\Request;

class OpeningBalanceController extends Controller
{
    private const TABS = [
        'accounts' => [
            'label' => 'Accounts',
            'icon' => 'bi-journal-text',
            'permission' => ['accounts', 'view'],
            'save_permission' => ['accounts', 'edit'],
        ],
        'items' => [
            'label' => 'Items',
            'icon' => 'bi-box-seam',
            'permission' => ['inventory', 'view'],
            'save_permission' => ['inventory', 'edit'],
        ],
        'customers' => [
            'label' => 'Customers',
            'icon' => 'bi-people',
            'permission' => ['ar', 'view'],
            'save_permission' => ['ar', 'edit'],
        ],
        'vendors' => [
            'label' => 'Vendors',
            'icon' => 'bi-truck',
            'permission' => ['ap', 'view'],
            'save_permission' => ['ap', 'edit'],
        ],
    ];

    public function __construct(private readonly OpeningBalanceService $openingBalances)
    {
    }

    public function index(Request $request)
    {
        $tab = $request->query('tab', 'accounts');
        if (!isset(self::TABS[$tab])) {
            $tab = 'accounts';
        }

        $visibleTabs = $this->visibleTabs();
        if ($visibleTabs->isEmpty()) {
            abort(403, 'Insufficient permissions.');
        }

        if (!$visibleTabs->has($tab)) {
            $tab = $visibleTabs->keys()->first();
        }

        $tabConfig = self::TABS[$tab];
        $this->gate($tabConfig['permission'][0], $tabConfig['permission'][1]);

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

        $accountType = $request->query('account_type');
        if ($accountType && !in_array($accountType, QuickBooksChartOfAccounts::openingBalanceAccountTypeKeys(), true)) {
            $accountType = null;
        }

        $accountFilters = [
            'section' => $accountType,
        ];

        $itemFilters = [
            'warehouse_id' => $request->query('warehouse'),
            'type' => $request->query('item_type'),
            'category_id' => $request->query('category'),
        ];

        $data = match ($tab) {
            'accounts' => [
                'rows' => $this->openingBalances->accounts($companyId, $accountFilters),
                'accountFilters' => $accountFilters,
                'accountTypeGroups' => QuickBooksChartOfAccounts::openingBalanceAccountTypeGroups(),
            ],
            'items' => [
                'rows' => $this->openingBalances->items($companyId, $itemFilters),
                'warehouses' => Warehouse::withoutGlobalScopes()
                    ->where('company_id', $companyId)
                    ->whereNull('deleted_at')
                    ->where('is_active', true)
                    ->orderBy('name')
                    ->get(['id', 'code', 'name']),
                'categories' => ItemCategory::withoutGlobalScopes()
                    ->where('company_id', $companyId)
                    ->whereNull('deleted_at')
                    ->orderBy('name')
                    ->get(['id', 'name', 'depth']),
                'itemFilters' => $itemFilters,
                'itemTypes' => [
                    'inventory' => 'Inventory',
                    'service' => 'Service',
                    'non_inventory' => 'Non-Inventory',
                ],
            ],
            'customers' => ['rows' => $this->openingBalances->customers($companyId)],
            'vendors' => ['rows' => $this->openingBalances->vendors($companyId)],
        };

        $savedAsOf = $data['rows']->pluck('as_of_date')->filter()->first();

        return view('masters.opening-balance.index', array_merge($data, [
            'tabs' => $visibleTabs->all(),
            'activeTab' => $tab,
            'canSave' => auth()->user()->hasPermission($tabConfig['save_permission'][0], $tabConfig['save_permission'][1]),
            'asOfDate' => old('as_of_date', $savedAsOf ?: now()->toDateString()),
            'currencySymbol' => Setting::get($companyId, 'currency_symbol', '$'),
        ]));
    }

    public function store(Request $request, string $tab)
    {
        if (!isset(self::TABS[$tab])) {
            abort(404);
        }

        $tabConfig = self::TABS[$tab];
        $this->gate($tabConfig['save_permission'][0], $tabConfig['save_permission'][1]);

        $rules = [
            'as_of_date' => 'nullable|date',
            'rows' => 'required|array',
            'rows.*.entity_id' => 'required|integer|min:1',
            'rows.*.balance' => 'nullable|numeric',
            'rows.*.quantity' => 'nullable|numeric|min:0',
            'rows.*.unit_cost' => 'nullable|numeric|min:0',
        ];

        if ($tab === 'items') {
            $rules['filter_warehouse'] = 'required|integer|min:1';
            $rules['rows.*.warehouse_id'] = 'required|integer|min:1';
            $rules['rows.*.rate'] = 'nullable|numeric|min:0';
        }

        $validated = $request->validate($rules);

        $companyId = auth()->user()->company_id;
        $asOfDate = $validated['as_of_date'] ?? null;

        try {
            $saved = match ($tab) {
                'accounts' => $this->openingBalances->saveAccounts($companyId, $validated['rows'], $asOfDate),
                'items' => $this->openingBalances->saveItems($companyId, $validated['rows'], $asOfDate),
                'customers' => $this->openingBalances->saveCustomers($companyId, $validated['rows'], $asOfDate),
                'vendors' => $this->openingBalances->saveVendors($companyId, $validated['rows'], $asOfDate),
            };
        } catch (\RuntimeException $e) {
            return back()
                ->withInput()
                ->with('error', $e->getMessage());
        }

        AuditLog::record('update', 'opening_balance', $tab, null, ['saved' => $saved], "Opening balances saved and posted: {$tab}");

        $redirectParams = ['tab' => $tab];

        if ($tab === 'accounts' && $request->filled('filter_account_type')) {
            $redirectParams['account_type'] = $request->input('filter_account_type');
        }

        if ($tab === 'items') {
            $redirectParams['warehouse'] = $validated['filter_warehouse'] ?? $request->input('filter_warehouse');
            if ($request->filled('filter_item_type')) {
                $redirectParams['item_type'] = $request->input('filter_item_type');
            }
            if ($request->filled('filter_category')) {
                $redirectParams['category'] = $request->input('filter_category');
            }
        }

        return redirect()
            ->route('masters.opening-balance.index', $redirectParams)
            ->with('success', ucfirst($tab) . " opening balances saved and journal posted ({$saved} records).");
    }

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

    private function visibleTabs(): \Illuminate\Support\Collection
    {
        return collect(self::TABS)->filter(function (array $tab) {
            return auth()->user()->hasPermission($tab['permission'][0], $tab['permission'][1]);
        });
    }
}
