<?php

namespace App\Http\Controllers\Admin\Accounting;

use App\Enums\AccountCategory;
use App\Enums\AccountStatus;
use App\Http\Controllers\Admin\PageController;
use App\Models\Account;
use App\Services\Accounting\AccountBalanceService;
use App\Services\Accounting\OpeningBalanceService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class ChartOfAccountsController extends PageController
{
    public function __construct(
        private readonly OpeningBalanceService $openingBalanceService,
        private readonly AccountBalanceService $accountBalanceService,
    ) {}

    public function index(): View
    {
        $accounts = Account::query()
            ->orderBy('code')
            ->get()
            ->map(function (Account $account) {
                return [
                    'account' => $account,
                    'balance' => $this->accountBalanceService->getBalance($account),
                ];
            });

        $accountsByCategory = $accounts->groupBy(
            fn (array $row) => $row['account']->category->label(),
        );

        return $this->page('admin.coa.index', [
            'crumbTitle' => 'Chart of Accounts',
            'crumbSub' => 'Master list of all financial accounts',
            'accountsByCategory' => $accountsByCategory,
        ]);
    }

    public function create(): View
    {
        return $this->page('admin.coa.create', [
            'crumbTitle' => 'Chart of Accounts',
            'crumbSub' => 'New account',
            'categories' => AccountCategory::cases(),
            'statuses' => AccountStatus::cases(),
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $this->validateAccount($request);
        $category = AccountCategory::from($validated['category']);

        DB::transaction(function () use ($validated, $category) {
            $account = Account::query()->create([
                ...$validated,
                'normal_balance_side' => $category->normalBalanceSide(),
            ]);

            $this->openingBalanceService->postForAccount($account);
        });

        return $this->redirectWithSuccess('accounting.coa.index', 'Account created successfully.');
    }

    public function edit(Account $account): View
    {
        return $this->page('admin.coa.edit', [
            'crumbTitle' => 'Chart of Accounts',
            'crumbSub' => 'Edit '.$account->code,
            'account' => $account,
            'categories' => AccountCategory::cases(),
            'statuses' => AccountStatus::cases(),
        ]);
    }

    public function update(Request $request, Account $account): RedirectResponse
    {
        $validated = $this->validateAccount($request, $account);
        $category = AccountCategory::from($validated['category']);
        $previousOpeningBalance = (string) $account->opening_balance;

        DB::transaction(function () use ($account, $validated, $category, $previousOpeningBalance) {
            $account->update([
                ...$validated,
                'normal_balance_side' => $category->normalBalanceSide(),
            ]);

            if (bccomp((string) $account->opening_balance, $previousOpeningBalance, 2) !== 0) {
                if (bccomp($previousOpeningBalance, '0.00', 2) === 0) {
                    $this->openingBalanceService->postForAccount($account->fresh());
                } else {
                    $this->openingBalanceService->postAdjustment($account->fresh(), $previousOpeningBalance);
                }
            }
        });

        return $this->redirectWithSuccess('accounting.coa.index', 'Account updated successfully.');
    }

    public function destroy(Account $account): RedirectResponse
    {
        if ($account->journalLines()->exists()) {
            return back()->with('error', 'Cannot delete an account with journal activity.');
        }

        $account->delete();

        return $this->redirectWithSuccess('accounting.coa.index', 'Account deleted.');
    }

    /**
     * @return array<string, mixed>
     */
    private function validateAccount(Request $request, ?Account $account = null): array
    {
        return $request->validate([
            'code' => [
                'required',
                'string',
                'max:50',
                Rule::unique('accounts', 'code')->ignore($account?->id),
            ],
            'name' => ['required', 'string', 'max:255'],
            'category' => ['required', Rule::enum(AccountCategory::class)],
            'account_type' => ['nullable', 'string', 'max:100'],
            'opening_balance' => ['nullable', 'numeric'],
            'opening_balance_date' => ['nullable', 'date'],
            'status' => ['required', Rule::enum(AccountStatus::class)],
            'description' => ['nullable', 'string'],
        ]);
    }
}
