<?php

namespace App\Http\Controllers\Company\Banking;

use App\Http\Controllers\Controller;
use App\Models\Account;
use App\Models\AuditLog;
use App\Models\ChequeBook;
use App\Models\Setting;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;

class ChequeBooksController extends Controller
{
    private function companyId(): int
    {
        return auth()->user()->company_id;
    }

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

    private function requireChequeRegister(): void
    {
        if (Setting::get($this->companyId(), 'use_cheque_register', '0') !== '1') {
            abort(403, 'Enable Use Cheque Register in Accounting Settings to access this feature.');
        }
    }

    private function sameCompany(ChequeBook $chequeBook): void
    {
        if ($chequeBook->company_id !== $this->companyId()) {
            abort(403);
        }
    }

    public function index()
    {
        $this->gate('view');
        $this->requireChequeRegister();

        $companyId = $this->companyId();

        return view('banking.cheque-books.index', [
            'books' => ChequeBook::query()
                ->with('account:id,code,name')
                ->orderByDesc('id')
                ->get(),
            'bankAccounts' => $this->bankAccounts($companyId),
        ]);
    }

    public function store(Request $request)
    {
        $this->gate('view');
        $this->requireChequeRegister();

        $companyId = $this->companyId();
        $validated = $this->validateBook($request, $companyId);
        $this->assertBankAccount($companyId, (int) $validated['account_id']);

        $book = ChequeBook::withoutGlobalScopes()->create([
            'company_id' => $companyId,
            ...$validated,
        ]);

        AuditLog::record('create', 'cheque_books', (string) $book->id, null, [
            'bank' => $book->bank,
            'book_no' => $book->book_no,
        ], "Cheque book created: {$book->bank} / Book {$book->book_no}");

        return redirect()
            ->route('banking.cheque-books.index')
            ->with('success', 'Cheque book saved.');
    }

    public function update(Request $request, ChequeBook $chequeBook)
    {
        $this->gate('view');
        $this->requireChequeRegister();
        $this->sameCompany($chequeBook);

        $companyId = $this->companyId();
        $validated = $this->validateBook($request, $companyId, $chequeBook->id);
        $this->assertBankAccount($companyId, (int) $validated['account_id']);

        $chequeBook->update($validated);

        AuditLog::record('update', 'cheque_books', (string) $chequeBook->id, null, [
            'bank' => $chequeBook->bank,
            'book_no' => $chequeBook->book_no,
        ], "Cheque book updated: {$chequeBook->bank} / Book {$chequeBook->book_no}");

        return redirect()
            ->route('banking.cheque-books.index')
            ->with('success', 'Cheque book updated.');
    }

    public function destroy(ChequeBook $chequeBook)
    {
        $this->gate('view');
        $this->requireChequeRegister();
        $this->sameCompany($chequeBook);

        $label = "{$chequeBook->bank} / Book {$chequeBook->book_no}";
        $chequeBook->delete();

        AuditLog::record('delete', 'cheque_books', (string) $chequeBook->id, null, null, "Cheque book deleted: {$label}");

        return redirect()
            ->route('banking.cheque-books.index')
            ->with('success', 'Cheque book deleted.');
    }

    private function bankAccounts(int $companyId)
    {
        return Account::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->postable()
            ->where('is_bank', true)
            ->where('is_cash', false)
            ->orderBy('code')
            ->orderBy('name')
            ->get(['id', 'code', 'name']);
    }

    private function assertBankAccount(int $companyId, int $accountId): void
    {
        $exists = Account::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('id', $accountId)
            ->postable()
            ->where('is_bank', true)
            ->where('is_cash', false)
            ->exists();

        if (!$exists) {
            throw ValidationException::withMessages([
                'account_id' => 'Select a valid Bank Account ledger from the chart of accounts.',
            ]);
        }
    }

    private function validateBook(Request $request, int $companyId, ?int $ignoreId = null): array
    {
        $validated = $request->validate([
            'account_id' => 'required|integer|exists:accounts,id',
            'bank' => 'required|string|max:100',
            'branch' => 'nullable|string|max:100',
            'short_account_no' => 'nullable|string|max:50',
            'book_no' => [
                'required',
                'string',
                'max:50',
                Rule::unique('cheque_books', 'book_no')
                    ->where(fn ($query) => $query
                        ->where('company_id', $companyId)
                        ->where('account_id', $request->input('account_id')))
                    ->ignore($ignoreId),
            ],
            'leaf_start' => 'required|integer|min:1',
            'leaf_end' => 'required|integer|min:1|gte:leaf_start',
        ]);

        $validated['branch'] = $validated['branch'] ?? null;
        $validated['short_account_no'] = $validated['short_account_no'] ?? null;
        $validated['total_leaves'] = ChequeBook::calculateTotalLeaves(
            (int) $validated['leaf_start'],
            (int) $validated['leaf_end'],
        );

        return $validated;
    }
}
