<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Account;
use App\Models\Expense;
use App\Models\User;
use App\Models\Vendor;
use App\Notifications\ExpenseReviewedNotification;
use App\Notifications\ExpenseSubmittedNotification;
use App\Services\Finance\ExpenseScopeService;
use App\Services\Finance\FinanceNavService;
use App\Services\HrmsNotificationService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;

class ExpenseController extends Controller
{
    public function index(Request $request, ExpenseScopeService $scopes)
    {
        $this->authorize('viewAny', Expense::class);

        $user = Auth::user();

        if (! $request->filled('scope')) {
            return redirect()->route('expenses.index', array_merge(
                $request->only(['status', 'page']),
                ['scope' => $scopes->defaultScope($user)]
            ));
        }

        $expenseScope = $scopes->resolveScope($user, $request->input('scope'));
        $query = Expense::with(['account', 'claimant', 'vendor'])->orderByDesc('date');

        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }

        $scopes->applyScope($query, $user, $expenseScope);

        $expenses = $query->paginate(15)->withQueryString();
        $expenseScopeOptions = app(FinanceNavService::class)->availableExpenseScopes($user);

        return view('finance.expenses', compact('expenses', 'expenseScope', 'expenseScopeOptions'));
    }

    public function create(FinanceNavService $nav)
    {
        $this->authorize('create', Expense::class);

        $user = Auth::user();
        $showTreasuryFields = $nav->canUseTreasuryMasters($user);
        $accounts = $showTreasuryFields ? Account::orderBy('name')->get() : collect();
        $vendors = $showTreasuryFields ? Vendor::orderBy('name')->get() : collect();
        $canAssignClaimant = $nav->canViewCompanyExpenses($user);
        $employees = collect();

        if ($canAssignClaimant && $user?->company_id) {
            $employees = User::forActiveDirectory()
                ->where('company_id', $user->company_id)
                ->orderBy('name')
                ->get(['id', 'name']);
        }

        return view('finance.expenses-create', compact('accounts', 'vendors', 'canAssignClaimant', 'employees', 'showTreasuryFields'));
    }

    public function store(Request $request, HrmsNotificationService $notifier, FinanceNavService $nav, ExpenseScopeService $scopes)
    {
        $this->authorize('create', Expense::class);

        $user = Auth::user();
        $isFinance = $nav->canViewCompanyExpenses($user);
        $showTreasuryFields = $nav->canUseTreasuryMasters($user);

        $rules = [
            'vendor_id' => 'nullable|exists:vendors,id',
            'category' => 'required|string|max:191',
            'amount' => 'required|numeric|min:0',
            'date' => 'required|date',
            'description' => 'nullable|string|max:1000',
        ];

        if ($showTreasuryFields) {
            $rules['account_id'] = 'nullable|exists:accounts,id';
        }

        if ($isFinance) {
            $rules['user_id'] = 'nullable|exists:users,id';
            $rules['status'] = 'nullable|in:pending,approved,rejected,paid';
        }

        $validated = $request->validate($rules);

        $claimantId = $isFinance && ! empty($validated['user_id'])
            ? (int) $validated['user_id']
            : (int) $user->id;

        $expense = Expense::create([
            'company_id' => $user->company_id,
            'user_id' => $claimantId,
            'submitted_by' => $user->id,
            'account_id' => $showTreasuryFields ? ($validated['account_id'] ?? null) : null,
            'vendor_id' => $showTreasuryFields ? ($validated['vendor_id'] ?? null) : null,
            'category' => $validated['category'],
            'amount' => $validated['amount'],
            'date' => $validated['date'],
            'description' => $validated['description'] ?? null,
            'status' => $isFinance ? ($validated['status'] ?? 'pending') : 'pending',
        ]);

        if ($expense->status === 'pending') {
            $expense->load('claimant');
            $notifier->notifyExpenseApprovers(
                (int) $user->company_id,
                new ExpenseSubmittedNotification($expense),
                [(int) $user->id]
            );
        }

        $redirectScope = $isFinance ? 'company' : ($user->hasPermission('finance_team_view') ? 'team' : 'self');

        return redirect()
            ->route('expenses.index', ['scope' => $redirectScope])
            ->with('success', 'Expense submitted successfully. Finance will be notified.');
    }

    public function edit(Expense $expense, FinanceNavService $nav)
    {
        $this->authorize('view', $expense);

        if ($expense->isLocked()) {
            return redirect()->route('expenses.show', ['expense' => $expense, 'scope' => request('scope')]);
        }

        $this->authorize('update', $expense);

        $showTreasuryFields = $nav->canUseTreasuryMasters(Auth::user());
        $accounts = $showTreasuryFields ? Account::orderBy('name')->get() : collect();
        $vendors = $showTreasuryFields ? Vendor::orderBy('name')->get() : collect();
        $canReview = Auth::user()?->canApproveExpense($expense) ?? false;

        return view('finance.expenses-edit', compact('expense', 'accounts', 'vendors', 'canReview', 'showTreasuryFields'));
    }

    public function show(Expense $expense)
    {
        $this->authorize('view', $expense);

        return view('finance.expenses-show', compact('expense'));
    }

    public function update(Request $request, Expense $expense)
    {
        if ($expense->isLocked()) {
            abort(403, 'Paid or rejected expenses cannot be edited.');
        }

        $this->authorize('update', $expense);

        $user = Auth::user();
        $nav = app(FinanceNavService::class);
        $canReview = $user->canApproveExpense($expense);
        $showTreasuryFields = $nav->canUseTreasuryMasters($user);

        $rules = [
            'vendor_id' => 'nullable|exists:vendors,id',
            'category' => 'required|string|max:191',
            'amount' => 'required|numeric|min:0',
            'date' => 'required|date',
            'description' => 'nullable|string|max:1000',
        ];

        if ($showTreasuryFields) {
            $rules['account_id'] = 'nullable|exists:accounts,id';
        }

        if ($canReview) {
            $rules['status'] = 'required|in:pending,approved,rejected,paid';
            $rules['review_remarks'] = 'nullable|string|max:1000';
            $rules['deduct_from_payroll'] = 'nullable|boolean';
        }

        $validated = $request->validate($rules);
        $previousStatus = $expense->status;

        $payload = [
            'vendor_id' => $showTreasuryFields ? ($validated['vendor_id'] ?? null) : $expense->vendor_id,
            'category' => $validated['category'],
            'amount' => $validated['amount'],
            'date' => $validated['date'],
            'description' => $validated['description'] ?? null,
        ];

        if ($showTreasuryFields) {
            $payload['account_id'] = $validated['account_id'] ?? null;
        }

        if ($canReview && isset($validated['status'])) {
            $payload['status'] = $validated['status'];
            $payload['review_remarks'] = $validated['review_remarks'] ?? null;
            $payload['deduct_from_payroll'] = $request->boolean('deduct_from_payroll');
            if ($previousStatus !== $validated['status']) {
                $payload['reviewed_by'] = $user->id;
                $payload['reviewed_at'] = now();
            }
        }

        $expense->update($payload);
        $expense->load('claimant');

        if ($canReview && $previousStatus !== $expense->status && in_array($expense->status, ['approved', 'rejected', 'paid'], true)) {
            $expense->claimant?->notify(new ExpenseReviewedNotification($expense, $expense->status));
        }

        return redirect()->route('expenses.index', ['scope' => request('scope', app(ExpenseScopeService::class)->defaultScope($user))])
            ->with('success', 'Expense updated successfully.');
    }

    public function updateStatus(Request $request, Expense $expense)
    {
        $this->authorize('review', $expense);

        $validated = $request->validate([
            'status' => ['required', Rule::in(['approved', 'rejected', 'paid'])],
            'review_remarks' => 'nullable|string|max:1000',
            'deduct_from_payroll' => 'nullable|boolean',
        ]);

        $previousStatus = $expense->status;

        $expense->update([
            'status' => $validated['status'],
            'review_remarks' => $validated['review_remarks'] ?? null,
            'deduct_from_payroll' => $request->boolean('deduct_from_payroll'),
            'reviewed_by' => Auth::id(),
            'reviewed_at' => now(),
        ]);

        $expense->load('claimant');

        if ($previousStatus !== $expense->status) {
            $expense->claimant?->notify(new ExpenseReviewedNotification($expense, $expense->status));
        }

        return redirect()->back()->with('success', 'Expense status updated.');
    }

    public function destroy(Expense $expense)
    {
        $this->authorize('delete', $expense);
        $expense->delete();

        return redirect()->route('expenses.index', ['scope' => request('scope', 'self')])
            ->with('success', 'Expense deleted successfully.');
    }
}
