<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

class AdminFinanceController extends Controller
{
    private function adminOnly(): void
    {
        abort_unless(Auth::check() && Auth::user()->isAdmin(), 403, 'Admin access only.');
    }

    private function money(float|int|string|null $value): float
    {
        return round((float) ($value ?? 0), 2);
    }

    private function nextNumber(string $prefix, string $table, string $column): string
    {
        $date = now()->format('Ymd');
        $count = DB::table($table)->whereDate('created_at', today())->count() + 1;
        return $prefix . '-' . $date . '-' . str_pad((string) $count, 4, '0', STR_PAD_LEFT);
    }

    private function log(string $action, string $module, ?int $recordId = null, ?string $details = null): void
    {
        DB::table('audit_logs')->insert([
            'user_id' => Auth::id(),
            'action' => $action,
            'module' => $module,
            'record_id' => $recordId,
            'details' => $details,
            'ip_address' => request()->ip(),
            'created_at' => now(),
            'updated_at' => now(),
        ]);
    }

    private function accountId(string $code): ?int
    {
        return DB::table('chart_of_accounts')->where('code', $code)->value('id');
    }

    private function applyLineToAccount(int $accountId, float $debit, float $credit): void
    {
        $account = DB::table('chart_of_accounts')->where('id', $accountId)->first();
        if (! $account) {
            return;
        }

        $delta = in_array($account->type, ['asset', 'expense'], true)
            ? ($debit - $credit)
            : ($credit - $debit);

        DB::table('chart_of_accounts')->where('id', $accountId)->update([
            'current_balance' => DB::raw('current_balance + ' . $delta),
            'updated_at' => now(),
        ]);
    }

    private function postJournal(string $memo, int $debitAccountId, int $creditAccountId, float $amount, ?string $sourceType = null, ?int $sourceId = null, ?string $reference = null): int
    {
        return DB::transaction(function () use ($memo, $debitAccountId, $creditAccountId, $amount, $sourceType, $sourceId, $reference) {
            $entryId = DB::table('journal_entries')->insertGetId([
                'entry_number' => $this->nextNumber('JE', 'journal_entries', 'entry_number'),
                'entry_date' => now()->toDateString(),
                'reference' => $reference,
                'memo' => $memo,
                'source_type' => $sourceType,
                'source_id' => $sourceId,
                'created_by' => Auth::id(),
                'status' => 'posted',
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            DB::table('journal_lines')->insert([
                [
                    'journal_entry_id' => $entryId,
                    'chart_of_account_id' => $debitAccountId,
                    'description' => $memo,
                    'debit' => $amount,
                    'credit' => 0,
                    'created_at' => now(),
                    'updated_at' => now(),
                ],
                [
                    'journal_entry_id' => $entryId,
                    'chart_of_account_id' => $creditAccountId,
                    'description' => $memo,
                    'debit' => 0,
                    'credit' => $amount,
                    'created_at' => now(),
                    'updated_at' => now(),
                ],
            ]);

            $this->applyLineToAccount($debitAccountId, $amount, 0);
            $this->applyLineToAccount($creditAccountId, 0, $amount);
            return $entryId;
        });
    }

    public function dashboard(): View
    {
        $this->adminOnly();
        $summary = [
            'cash' => DB::table('chart_of_accounts')->where('is_cash_account', true)->sum('current_balance'),
            'receivables' => DB::table('chart_of_accounts')->where('code', '1100')->value('current_balance') ?? 0,
            'payables' => DB::table('chart_of_accounts')->where('code', '2000')->value('current_balance') ?? 0,
            'income' => DB::table('chart_of_accounts')->where('type', 'income')->sum('current_balance'),
            'expenses' => DB::table('chart_of_accounts')->where('type', 'expense')->sum('current_balance'),
            'pending_claims' => DB::table('expense_claims')->where('status', 'submitted')->count(),
            'unpaid_invoices' => DB::table('invoices')->whereIn('status', ['unpaid', 'partial'])->count(),
            'unpaid_bills' => DB::table('bills')->whereIn('status', ['unpaid', 'partial'])->count(),
        ];

        $recentPayments = DB::table('finance_payments')->latest()->limit(5)->get();
        $recentClaims = DB::table('expense_claims')->join('users', 'users.id', '=', 'expense_claims.user_id')
            ->select('expense_claims.*', 'users.name as user_name')->latest('expense_claims.created_at')->limit(5)->get();

        return view('admin.dashboard', compact('summary', 'recentPayments', 'recentClaims'));
    }

    public function accounts(): View
    {
        $this->adminOnly();
        $accounts = DB::table('chart_of_accounts')->orderBy('code')->get();
        return view('admin.accounts', compact('accounts'));
    }

    public function storeAccount(Request $request): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'code' => ['required', 'string', 'max:30', 'unique:chart_of_accounts,code'],
            'name' => ['required', 'string', 'max:160'],
            'type' => ['required', 'in:asset,liability,equity,income,expense'],
            'subtype' => ['nullable', 'string', 'max:120'],
            'opening_balance' => ['nullable', 'numeric'],
            'is_cash_account' => ['nullable', 'boolean'],
        ]);

        $opening = $this->money($data['opening_balance'] ?? 0);
        $id = DB::table('chart_of_accounts')->insertGetId([
            'code' => $data['code'],
            'name' => $data['name'],
            'type' => $data['type'],
            'subtype' => $data['subtype'] ?? null,
            'opening_balance' => $opening,
            'current_balance' => $opening,
            'is_cash_account' => $request->boolean('is_cash_account'),
            'is_system' => false,
            'status' => 'active',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $this->log('created', 'chart_of_accounts', $id, $data['name']);
        return back()->with('success', 'Account created.');
    }

    public function contacts(): View
    {
        $this->adminOnly();
        $contacts = DB::table('finance_contacts')->latest()->get();
        return view('admin.contacts', compact('contacts'));
    }

    public function storeContact(Request $request): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'type' => ['required', 'in:supplier,customer,employee,other'],
            'name' => ['required', 'string', 'max:180'],
            'email' => ['nullable', 'email', 'max:180'],
            'phone' => ['nullable', 'string', 'max:60'],
            'tax_number' => ['nullable', 'string', 'max:80'],
            'address' => ['nullable', 'string'],
        ]);

        $id = DB::table('finance_contacts')->insertGetId(array_merge($data, [
            'status' => 'active',
            'created_at' => now(),
            'updated_at' => now(),
        ]));
        $this->log('created', 'finance_contacts', $id, $data['name']);
        return back()->with('success', 'Contact created.');
    }

    public function banks(): View
    {
        $this->adminOnly();
        $banks = DB::table('bank_accounts')->leftJoin('chart_of_accounts', 'chart_of_accounts.id', '=', 'bank_accounts.chart_of_account_id')
            ->select('bank_accounts.*', 'chart_of_accounts.name as account_name_linked', 'chart_of_accounts.code as account_code')->latest('bank_accounts.created_at')->get();
        $cashAccounts = DB::table('chart_of_accounts')->where('type', 'asset')->orderBy('code')->get();
        return view('admin.banks', compact('banks', 'cashAccounts'));
    }

    public function storeBank(Request $request): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'chart_of_account_id' => ['nullable', 'exists:chart_of_accounts,id'],
            'bank_name' => ['required', 'string', 'max:160'],
            'account_name' => ['required', 'string', 'max:160'],
            'account_number' => ['nullable', 'string', 'max:80'],
            'currency' => ['required', 'string', 'max:10'],
            'opening_balance' => ['nullable', 'numeric'],
        ]);

        $opening = $this->money($data['opening_balance'] ?? 0);
        $id = DB::table('bank_accounts')->insertGetId(array_merge($data, [
            'opening_balance' => $opening,
            'current_balance' => $opening,
            'status' => 'active',
            'created_at' => now(),
            'updated_at' => now(),
        ]));
        $this->log('created', 'bank_accounts', $id, $data['bank_name']);
        return back()->with('success', 'Bank account created.');
    }

    public function invoices(): View
    {
        $this->adminOnly();
        $invoices = DB::table('invoices')->leftJoin('finance_contacts', 'finance_contacts.id', '=', 'invoices.contact_id')
            ->select('invoices.*', 'finance_contacts.name as contact_name')->latest('invoices.created_at')->get();
        $customers = DB::table('finance_contacts')->whereIn('type', ['customer', 'other'])->orderBy('name')->get();
        $banks = DB::table('bank_accounts')->orderBy('bank_name')->get();
        return view('admin.invoices', compact('invoices', 'customers', 'banks'));
    }

    public function storeInvoice(Request $request): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'contact_id' => ['nullable', 'exists:finance_contacts,id'],
            'invoice_date' => ['required', 'date'],
            'due_date' => ['nullable', 'date'],
            'subtotal' => ['required', 'numeric', 'min:0'],
            'tax_amount' => ['nullable', 'numeric', 'min:0'],
            'notes' => ['nullable', 'string'],
        ]);
        $subtotal = $this->money($data['subtotal']);
        $tax = $this->money($data['tax_amount'] ?? 0);
        $total = $subtotal + $tax;

        $id = DB::table('invoices')->insertGetId([
            'invoice_number' => $this->nextNumber('INV', 'invoices', 'invoice_number'),
            'contact_id' => $data['contact_id'] ?? null,
            'invoice_date' => $data['invoice_date'],
            'due_date' => $data['due_date'] ?? null,
            'subtotal' => $subtotal,
            'tax_amount' => $tax,
            'total_amount' => $total,
            'amount_paid' => 0,
            'status' => 'unpaid',
            'notes' => $data['notes'] ?? null,
            'created_by' => Auth::id(),
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $this->postJournal('Invoice created', $this->accountId('1100'), $this->accountId('4000'), $total, 'invoice', $id);
        $this->log('created', 'invoices', $id, 'Invoice total: ' . $total);
        return back()->with('success', 'Invoice created and posted to receivables.');
    }

    public function receiveInvoicePayment(Request $request, int $invoice): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'bank_account_id' => ['required', 'exists:bank_accounts,id'],
            'amount' => ['required', 'numeric', 'min:1'],
            'method' => ['required', 'string', 'max:80'],
            'reference' => ['nullable', 'string', 'max:160'],
        ]);
        $record = DB::table('invoices')->where('id', $invoice)->first();
        abort_if(! $record, 404);
        $amount = min($this->money($data['amount']), $this->money($record->total_amount - $record->amount_paid));

        DB::transaction(function () use ($record, $data, $amount) {
            DB::table('finance_payments')->insert([
                'payment_number' => $this->nextNumber('RCPT', 'finance_payments', 'payment_number'),
                'payment_type' => 'receipt',
                'payment_date' => now()->toDateString(),
                'bank_account_id' => $data['bank_account_id'],
                'contact_id' => $record->contact_id,
                'source_type' => 'invoice',
                'source_id' => $record->id,
                'amount' => $amount,
                'method' => $data['method'],
                'reference' => $data['reference'] ?? null,
                'notes' => 'Invoice payment received',
                'created_by' => Auth::id(),
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            DB::table('bank_accounts')->where('id', $data['bank_account_id'])->update([
                'current_balance' => DB::raw('current_balance + ' . $amount),
                'updated_at' => now(),
            ]);

            $newPaid = $this->money($record->amount_paid + $amount);
            DB::table('invoices')->where('id', $record->id)->update([
                'amount_paid' => $newPaid,
                'status' => $newPaid >= $record->total_amount ? 'paid' : 'partial',
                'updated_at' => now(),
            ]);

            $bankAccountId = DB::table('bank_accounts')->where('id', $data['bank_account_id'])->value('chart_of_account_id') ?: $this->accountId('1000');
            $this->postJournal('Invoice payment received', $bankAccountId, $this->accountId('1100'), $amount, 'invoice_payment', $record->id, $data['reference'] ?? null);
        });

        return back()->with('success', 'Invoice payment received.');
    }

    public function bills(): View
    {
        $this->adminOnly();
        $bills = DB::table('bills')->leftJoin('finance_contacts', 'finance_contacts.id', '=', 'bills.contact_id')
            ->select('bills.*', 'finance_contacts.name as contact_name')->latest('bills.created_at')->get();
        $suppliers = DB::table('finance_contacts')->whereIn('type', ['supplier', 'other'])->orderBy('name')->get();
        $expenseAccounts = DB::table('chart_of_accounts')->where('type', 'expense')->orderBy('code')->get();
        $banks = DB::table('bank_accounts')->orderBy('bank_name')->get();
        return view('admin.bills', compact('bills', 'suppliers', 'expenseAccounts', 'banks'));
    }

    public function storeBill(Request $request): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'contact_id' => ['nullable', 'exists:finance_contacts,id'],
            'expense_account_id' => ['required', 'exists:chart_of_accounts,id'],
            'bill_date' => ['required', 'date'],
            'due_date' => ['nullable', 'date'],
            'subtotal' => ['required', 'numeric', 'min:0'],
            'tax_amount' => ['nullable', 'numeric', 'min:0'],
            'notes' => ['nullable', 'string'],
        ]);
        $subtotal = $this->money($data['subtotal']);
        $tax = $this->money($data['tax_amount'] ?? 0);
        $total = $subtotal + $tax;

        $id = DB::table('bills')->insertGetId([
            'bill_number' => $this->nextNumber('BILL', 'bills', 'bill_number'),
            'contact_id' => $data['contact_id'] ?? null,
            'bill_date' => $data['bill_date'],
            'due_date' => $data['due_date'] ?? null,
            'subtotal' => $subtotal,
            'tax_amount' => $tax,
            'total_amount' => $total,
            'amount_paid' => 0,
            'status' => 'unpaid',
            'notes' => $data['notes'] ?? null,
            'created_by' => Auth::id(),
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $this->postJournal('Supplier bill created', (int) $data['expense_account_id'], $this->accountId('2000'), $total, 'bill', $id);
        $this->log('created', 'bills', $id, 'Bill total: ' . $total);
        return back()->with('success', 'Bill created and posted to payables.');
    }

    public function payBill(Request $request, int $bill): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'bank_account_id' => ['required', 'exists:bank_accounts,id'],
            'amount' => ['required', 'numeric', 'min:1'],
            'method' => ['required', 'string', 'max:80'],
            'reference' => ['nullable', 'string', 'max:160'],
        ]);
        $record = DB::table('bills')->where('id', $bill)->first();
        abort_if(! $record, 404);
        $amount = min($this->money($data['amount']), $this->money($record->total_amount - $record->amount_paid));

        DB::transaction(function () use ($record, $data, $amount) {
            DB::table('finance_payments')->insert([
                'payment_number' => $this->nextNumber('PAY', 'finance_payments', 'payment_number'),
                'payment_type' => 'disbursement',
                'payment_date' => now()->toDateString(),
                'bank_account_id' => $data['bank_account_id'],
                'contact_id' => $record->contact_id,
                'source_type' => 'bill',
                'source_id' => $record->id,
                'amount' => $amount,
                'method' => $data['method'],
                'reference' => $data['reference'] ?? null,
                'notes' => 'Bill payment made',
                'created_by' => Auth::id(),
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            DB::table('bank_accounts')->where('id', $data['bank_account_id'])->update([
                'current_balance' => DB::raw('current_balance - ' . $amount),
                'updated_at' => now(),
            ]);

            $newPaid = $this->money($record->amount_paid + $amount);
            DB::table('bills')->where('id', $record->id)->update([
                'amount_paid' => $newPaid,
                'status' => $newPaid >= $record->total_amount ? 'paid' : 'partial',
                'updated_at' => now(),
            ]);

            $bankAccountId = DB::table('bank_accounts')->where('id', $data['bank_account_id'])->value('chart_of_account_id') ?: $this->accountId('1000');
            $this->postJournal('Bill payment made', $this->accountId('2000'), $bankAccountId, $amount, 'bill_payment', $record->id, $data['reference'] ?? null);
        });

        return back()->with('success', 'Bill payment recorded.');
    }

    public function claims(): View
    {
        $this->adminOnly();
        $claims = DB::table('expense_claims')
            ->join('users', 'users.id', '=', 'expense_claims.user_id')
            ->leftJoin('chart_of_accounts', 'chart_of_accounts.id', '=', 'expense_claims.chart_of_account_id')
            ->select('expense_claims.*', 'users.name as user_name', 'chart_of_accounts.name as account_name')
            ->latest('expense_claims.created_at')->get();
        $banks = DB::table('bank_accounts')->orderBy('bank_name')->get();
        return view('admin.claims', compact('claims', 'banks'));
    }

    public function updateClaim(Request $request, int $claim): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'status' => ['required', 'in:approved,rejected,paid'],
            'admin_note' => ['nullable', 'string'],
            'bank_account_id' => ['nullable', 'exists:bank_accounts,id'],
        ]);
        $record = DB::table('expense_claims')->where('id', $claim)->first();
        abort_if(! $record, 404);

        DB::transaction(function () use ($record, $data) {
            $update = [
                'status' => $data['status'],
                'admin_note' => $data['admin_note'] ?? null,
                'approved_by' => Auth::id(),
                'approved_at' => now(),
                'updated_at' => now(),
            ];
            DB::table('expense_claims')->where('id', $record->id)->update($update);

            if ($data['status'] === 'paid' && ! empty($data['bank_account_id'])) {
                $amount = $this->money($record->amount + $record->tax_amount);
                DB::table('finance_payments')->insert([
                    'payment_number' => $this->nextNumber('REIM', 'finance_payments', 'payment_number'),
                    'payment_type' => 'reimbursement',
                    'payment_date' => now()->toDateString(),
                    'bank_account_id' => $data['bank_account_id'],
                    'contact_id' => null,
                    'source_type' => 'expense_claim',
                    'source_id' => $record->id,
                    'amount' => $amount,
                    'method' => 'bank_transfer',
                    'reference' => $record->claim_number,
                    'notes' => 'Expense reimbursement',
                    'created_by' => Auth::id(),
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
                DB::table('bank_accounts')->where('id', $data['bank_account_id'])->update([
                    'current_balance' => DB::raw('current_balance - ' . $amount),
                    'updated_at' => now(),
                ]);
                $bankAccountId = DB::table('bank_accounts')->where('id', $data['bank_account_id'])->value('chart_of_account_id') ?: $this->accountId('1000');
                $expenseAccount = $record->chart_of_account_id ?: $this->accountId('5000');
                $this->postJournal('Expense claim paid', $expenseAccount, $bankAccountId, $amount, 'expense_claim', $record->id, $record->claim_number);
            }
        });

        return back()->with('success', 'Claim updated.');
    }

    public function advances(): View
    {
        $this->adminOnly();
        $advances = DB::table('cash_advances')
            ->join('users', 'users.id', '=', 'cash_advances.user_id')
            ->select('cash_advances.*', 'users.name as user_name')
            ->latest('cash_advances.created_at')->get();
        return view('admin.advances', compact('advances'));
    }

    public function updateAdvance(Request $request, int $advance): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'status' => ['required', 'in:approved,rejected,paid,retired'],
            'admin_note' => ['nullable', 'string'],
        ]);
        DB::table('cash_advances')->where('id', $advance)->update([
            'status' => $data['status'],
            'admin_note' => $data['admin_note'] ?? null,
            'approved_by' => Auth::id(),
            'approved_at' => now(),
            'updated_at' => now(),
        ]);
        $this->log('updated', 'cash_advances', $advance, $data['status']);
        return back()->with('success', 'Cash advance updated.');
    }

    public function journals(): View
    {
        $this->adminOnly();
        $journals = DB::table('journal_entries')->latest('entry_date')->limit(100)->get();
        $accounts = DB::table('chart_of_accounts')->where('status', 'active')->orderBy('code')->get();
        return view('admin.journals', compact('journals', 'accounts'));
    }

    public function storeJournal(Request $request): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'entry_date' => ['required', 'date'],
            'debit_account_id' => ['required', 'exists:chart_of_accounts,id'],
            'credit_account_id' => ['required', 'exists:chart_of_accounts,id', 'different:debit_account_id'],
            'amount' => ['required', 'numeric', 'min:1'],
            'reference' => ['nullable', 'string', 'max:120'],
            'memo' => ['required', 'string'],
        ]);

        $amount = $this->money($data['amount']);
        DB::transaction(function () use ($data, $amount) {
            $entryId = DB::table('journal_entries')->insertGetId([
                'entry_number' => $this->nextNumber('JE', 'journal_entries', 'entry_number'),
                'entry_date' => $data['entry_date'],
                'reference' => $data['reference'] ?? null,
                'memo' => $data['memo'],
                'source_type' => 'manual',
                'source_id' => null,
                'created_by' => Auth::id(),
                'status' => 'posted',
                'created_at' => now(),
                'updated_at' => now(),
            ]);
            DB::table('journal_lines')->insert([
                [
                    'journal_entry_id' => $entryId,
                    'chart_of_account_id' => $data['debit_account_id'],
                    'description' => $data['memo'],
                    'debit' => $amount,
                    'credit' => 0,
                    'created_at' => now(),
                    'updated_at' => now(),
                ],
                [
                    'journal_entry_id' => $entryId,
                    'chart_of_account_id' => $data['credit_account_id'],
                    'description' => $data['memo'],
                    'debit' => 0,
                    'credit' => $amount,
                    'created_at' => now(),
                    'updated_at' => now(),
                ],
            ]);
            $this->applyLineToAccount((int) $data['debit_account_id'], $amount, 0);
            $this->applyLineToAccount((int) $data['credit_account_id'], 0, $amount);
            $this->log('posted', 'journal_entries', $entryId, $data['memo']);
        });
        return back()->with('success', 'Manual journal posted.');
    }

    public function budgets(): View
    {
        $this->adminOnly();
        $budgets = DB::table('budgets')->leftJoin('chart_of_accounts', 'chart_of_accounts.id', '=', 'budgets.chart_of_account_id')
            ->select('budgets.*', 'chart_of_accounts.name as account_name')->latest('budgets.created_at')->get();
        $accounts = DB::table('chart_of_accounts')->orderBy('code')->get();
        return view('admin.budgets', compact('budgets', 'accounts'));
    }

    public function storeBudget(Request $request): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'department' => ['nullable', 'string', 'max:120'],
            'chart_of_account_id' => ['nullable', 'exists:chart_of_accounts,id'],
            'period_start' => ['required', 'date'],
            'period_end' => ['required', 'date', 'after_or_equal:period_start'],
            'allocated_amount' => ['required', 'numeric', 'min:0'],
            'notes' => ['nullable', 'string'],
        ]);
        $id = DB::table('budgets')->insertGetId(array_merge($data, [
            'budget_number' => $this->nextNumber('BUD', 'budgets', 'budget_number'),
            'spent_amount' => 0,
            'status' => 'active',
            'created_at' => now(),
            'updated_at' => now(),
        ]));
        $this->log('created', 'budgets', $id, $data['department'] ?? null);
        return back()->with('success', 'Budget created.');
    }

    public function payroll(): View
    {
        $this->adminOnly();
        $records = DB::table('payroll_records')->leftJoin('users', 'users.id', '=', 'payroll_records.user_id')
            ->select('payroll_records.*', 'users.name as user_name')->latest('payroll_records.created_at')->get();
        $users = User::where('status', 'active')->orderBy('name')->get();
        return view('admin.payroll', compact('records', 'users'));
    }

    public function storePayroll(Request $request): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'user_id' => ['required', 'exists:users,id'],
            'pay_period' => ['required', 'string', 'max:80'],
            'payment_date' => ['nullable', 'date'],
            'gross_pay' => ['required', 'numeric', 'min:0'],
            'deductions' => ['nullable', 'numeric', 'min:0'],
            'tax_amount' => ['nullable', 'numeric', 'min:0'],
            'notes' => ['nullable', 'string'],
        ]);
        $gross = $this->money($data['gross_pay']);
        $deductions = $this->money($data['deductions'] ?? 0);
        $tax = $this->money($data['tax_amount'] ?? 0);
        $net = $gross - $deductions - $tax;
        $id = DB::table('payroll_records')->insertGetId(array_merge($data, [
            'payroll_number' => $this->nextNumber('PAYROLL', 'payroll_records', 'payroll_number'),
            'gross_pay' => $gross,
            'deductions' => $deductions,
            'tax_amount' => $tax,
            'net_pay' => $net,
            'status' => 'draft',
            'created_by' => Auth::id(),
            'created_at' => now(),
            'updated_at' => now(),
        ]));
        $this->log('created', 'payroll_records', $id, $data['pay_period']);
        return back()->with('success', 'Payroll record created.');
    }

    public function taxes(): View
    {
        $this->adminOnly();
        $records = DB::table('tax_records')->latest('period_end')->get();
        return view('admin.taxes', compact('records'));
    }

    public function storeTax(Request $request): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'tax_type' => ['required', 'string', 'max:80'],
            'period_start' => ['required', 'date'],
            'period_end' => ['required', 'date', 'after_or_equal:period_start'],
            'taxable_amount' => ['required', 'numeric', 'min:0'],
            'tax_amount' => ['required', 'numeric', 'min:0'],
            'due_date' => ['nullable', 'date'],
            'notes' => ['nullable', 'string'],
        ]);
        $id = DB::table('tax_records')->insertGetId(array_merge($data, [
            'tax_number' => $this->nextNumber('TAX', 'tax_records', 'tax_number'),
            'status' => 'pending',
            'created_at' => now(),
            'updated_at' => now(),
        ]));
        $this->log('created', 'tax_records', $id, $data['tax_type']);
        return back()->with('success', 'Tax record created.');
    }

    public function reports(): View
    {
        $this->adminOnly();
        $accounts = DB::table('chart_of_accounts')->orderBy('code')->get();
        $income = $accounts->where('type', 'income')->sum('current_balance');
        $expenses = $accounts->where('type', 'expense')->sum('current_balance');
        $assets = $accounts->where('type', 'asset')->sum('current_balance');
        $liabilities = $accounts->where('type', 'liability')->sum('current_balance');
        $equity = $accounts->where('type', 'equity')->sum('current_balance');
        $profit = $income - $expenses;
        return view('admin.reports', compact('accounts', 'income', 'expenses', 'assets', 'liabilities', 'equity', 'profit'));
    }

    public function users(): View
    {
        $this->adminOnly();
        $users = User::latest()->get();
        return view('admin.users', compact('users'));
    }

    public function updateUser(Request $request, int $user): RedirectResponse
    {
        $this->adminOnly();
        $data = $request->validate([
            'role' => ['required', 'in:user,admin'],
            'status' => ['required', 'in:active,inactive,suspended'],
        ]);
        User::where('id', $user)->update($data);
        $this->log('updated', 'users', $user, 'Role/status changed');
        return back()->with('success', 'User updated.');
    }

    public function auditLogs(): View
    {
        $this->adminOnly();
        $logs = DB::table('audit_logs')->leftJoin('users', 'users.id', '=', 'audit_logs.user_id')
            ->select('audit_logs.*', 'users.name as user_name')->latest('audit_logs.created_at')->limit(150)->get();
        return view('admin.audit', compact('logs'));
    }
}
