<?php

namespace App\Http\Controllers\User;

use App\Http\Controllers\Controller;
use App\Models\Employee;
use App\Models\EmployeeAdvance;
use App\Models\EmployeeAdvanceRepayment;
use App\Models\UserAccount;
use App\Services\AccountLedgerService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class EmployeeAdvanceController extends Controller
{
    public function index(Request $request)
    {
        $query = EmployeeAdvance::query()
            ->where('user_id', auth()->id())
            ->with(['employee', 'account'])
            ->latest('advance_date')
            ->latest('id');

        if ($request->filled('search')) {
            $search = trim($request->search);

            $query->whereHas('employee', function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('employee_code', 'like', "%{$search}%")
                    ->orWhere('phone', 'like', "%{$search}%");
            });
        }

        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }

        if ($request->filled('employee_id')) {
            $query->where('employee_id', $request->employee_id);
        }

        $advances = $query->paginate(15)->withQueryString();

        $employees = Employee::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get();

        $totalAdvance = EmployeeAdvance::where('user_id', auth()->id())->sum('amount');

        $totalOutstanding = EmployeeAdvance::where('user_id', auth()->id())
            ->whereIn('status', ['active', 'partial_adjusted'])
            ->sum('remaining_amount');

        return view('user.hr.advances.index', compact(
            'advances',
            'employees',
            'totalAdvance',
            'totalOutstanding'
        ));
    }

    public function create()
    {
        $employees = Employee::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get();

        $accounts = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get();

        return view('user.hr.advances.create', compact('employees', 'accounts'));
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'employee_id'     => ['required', 'exists:employees,id'],
            'user_account_id' => ['required', 'exists:user_accounts,id'],
            'advance_date'    => ['required', 'date'],
            'amount'          => ['required', 'numeric', 'min:0.01'],
            'reference_no'    => ['nullable', 'string', 'max:255'],
            'note'            => ['nullable', 'string'],
        ]);

        $employee = Employee::where('user_id', auth()->id())
            ->where('status', 'active')
            ->findOrFail($validated['employee_id']);

        $account = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->findOrFail($validated['user_account_id']);

        DB::transaction(function () use ($validated, $employee, $account) {
            $advance = EmployeeAdvance::create([
                'user_id'          => auth()->id(),
                'employee_id'      => $employee->id,
                'user_account_id'  => $account->id,
                'advance_date'     => $validated['advance_date'],
                'amount'           => round((float) $validated['amount'], 2),
                'adjusted_amount'  => 0,
                'remaining_amount' => round((float) $validated['amount'], 2),
                'status'           => 'active',
                'reference_no'     => $this->normalizeText($validated['reference_no'] ?? null),
                'note'             => $this->normalizeText($validated['note'] ?? null),
            ]);

            $this->syncLedgers($advance, $employee->name);
        });

        $message = __('Employee_Advance_Controller.employee_advance_created_successfully');

        return redirect()
            ->route('user.hr.advances.index')
            ->with('toast_success', $message)
            ->with('ok', $message);
    }

    public function edit($id)
    {
        $advance = EmployeeAdvance::where('user_id', auth()->id())
            ->with(['employee', 'account'])
            ->findOrFail($id);

        if ((float) $advance->adjusted_amount > 0) {
            $message = __('Employee_Advance_Controller.adjusted_advance_cannot_be_edited');

            return redirect()
                ->route('user.hr.advances.index')
                ->with('toast_error', $message)
                ->with('err', $message);
        }

        $employees = Employee::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get();

        $accounts = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get();

        return view('user.hr.advances.edit', compact('advance', 'employees', 'accounts'));
    }

    public function update(Request $request, $id)
    {
        $advance = EmployeeAdvance::where('user_id', auth()->id())->findOrFail($id);

        if ((float) $advance->adjusted_amount > 0) {
            $message = __('Employee_Advance_Controller.adjusted_advance_cannot_be_edited');

            return redirect()
                ->route('user.hr.advances.index')
                ->with('toast_error', $message)
                ->with('err', $message);
        }

        $validated = $request->validate([
            'employee_id'     => ['required', 'exists:employees,id'],
            'user_account_id' => ['required', 'exists:user_accounts,id'],
            'advance_date'    => ['required', 'date'],
            'amount'          => ['required', 'numeric', 'min:0.01'],
            'reference_no'    => ['nullable', 'string', 'max:255'],
            'note'            => ['nullable', 'string'],
        ]);

        $employee = Employee::where('user_id', auth()->id())
            ->where('status', 'active')
            ->findOrFail($validated['employee_id']);

        $account = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->findOrFail($validated['user_account_id']);

        DB::transaction(function () use ($advance, $validated, $employee, $account) {
            $this->deleteLedgers($advance);

            $advance->update([
                'employee_id'      => $employee->id,
                'user_account_id'  => $account->id,
                'advance_date'     => $validated['advance_date'],
                'amount'           => round((float) $validated['amount'], 2),
                'adjusted_amount'  => 0,
                'remaining_amount' => round((float) $validated['amount'], 2),
                'status'           => 'active',
                'reference_no'     => $this->normalizeText($validated['reference_no'] ?? null),
                'note'             => $this->normalizeText($validated['note'] ?? null),
            ]);

            $this->syncLedgers($advance, $employee->name, true);
        });

        $message = __('Employee_Advance_Controller.employee_advance_updated_successfully');

        return redirect()
            ->route('user.hr.advances.index')
            ->with('toast_success', $message)
            ->with('ok', $message);
    }

    public function destroy($id)
    {
        $advance = EmployeeAdvance::where('user_id', auth()->id())->findOrFail($id);

        if ((float) $advance->adjusted_amount > 0) {
            $message = __('Employee_Advance_Controller.adjusted_advance_cannot_be_deleted');

            return redirect()
                ->route('user.hr.advances.index')
                ->with('toast_error', $message)
                ->with('err', $message);
        }

        DB::transaction(function () use ($advance) {
            $this->deleteLedgers($advance);
            $advance->delete();
        });

        $message = __('Employee_Advance_Controller.employee_advance_deleted_successfully_and_ledgers_adjusted');

        return redirect()
            ->route('user.hr.advances.index')
            ->with('toast_success', $message)
            ->with('ok', $message);
    }

    public function repaymentIndex(Request $request, $advanceId)
    {
        $advance = EmployeeAdvance::where('user_id', auth()->id())
            ->with(['employee', 'account'])
            ->findOrFail($advanceId);

        $query = EmployeeAdvanceRepayment::where('user_id', auth()->id())
            ->where('employee_advance_id', $advance->id)
            ->with(['account'])
            ->latest('repayment_date')
            ->latest('id');

        if ($request->filled('source')) {
            $query->where('source', $request->source);
        }

        if ($request->filled('account_id')) {
            $query->where('user_account_id', $request->account_id);
        }

        if ($request->filled('date_from')) {
            $query->whereDate('repayment_date', '>=', $request->date_from);
        }

        if ($request->filled('date_to')) {
            $query->whereDate('repayment_date', '<=', $request->date_to);
        }

        $repayments = $query->paginate(15)->withQueryString();

        $accounts = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get();

        $summaryBase = EmployeeAdvanceRepayment::where('user_id', auth()->id())
            ->where('employee_advance_id', $advance->id);

        $totalRepayment = (float) $summaryBase->sum('amount');

        $manualRepayment = (float) (clone $summaryBase)
            ->where(function ($q) {
                $q->whereNull('source')->orWhere('source', 'manual');
            })
            ->sum('amount');

        $payrollRepayment = (float) (clone $summaryBase)
            ->where('source', 'payroll_adjustment')
            ->sum('amount');

        return view('user.hr.advances.repayments.index', compact(
            'advance',
            'repayments',
            'accounts',
            'totalRepayment',
            'manualRepayment',
            'payrollRepayment'
        ));
    }

    public function repaymentCreate($advanceId)
    {
        $advance = EmployeeAdvance::where('user_id', auth()->id())
            ->with(['employee', 'account'])
            ->findOrFail($advanceId);

        if ((float) $advance->remaining_amount <= 0) {
            $message = __('Employee_Advance_Controller.no_due_left_for_this_advance');

            return redirect()
                ->route('user.hr.advances.index')
                ->with('toast_error', $message)
                ->with('err', $message);
        }

        $accounts = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get();

        return view('user.hr.advances.repayments.create', compact('advance', 'accounts'));
    }

    public function repaymentStore(Request $request, $advanceId)
    {
        $advance = EmployeeAdvance::where('user_id', auth()->id())
            ->with('employee')
            ->findOrFail($advanceId);

        if ((float) $advance->remaining_amount <= 0) {
            $message = __('Employee_Advance_Controller.no_due_left_for_this_advance');

            return redirect()
                ->route('user.hr.advances.index')
                ->with('toast_error', $message)
                ->with('err', $message);
        }

        $validated = $request->validate([
            'user_account_id' => ['required', 'exists:user_accounts,id'],
            'repayment_date'  => ['required', 'date'],
            'amount'          => ['required', 'numeric', 'min:0.01'],
            'reference_no'    => ['nullable', 'string', 'max:255'],
            'note'            => ['nullable', 'string'],
        ]);

        $account = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->findOrFail($validated['user_account_id']);

        $amount = round((float) $validated['amount'], 2);

        if ($amount > (float) $advance->remaining_amount) {
            return back()
                ->withErrors([
                    'amount' => __('Employee_Advance_Controller.repayment_amount_cannot_exceed_remaining'),
                ])
                ->withInput();
        }

        DB::transaction(function () use ($advance, $account, $validated, $amount) {
            $repayment = EmployeeAdvanceRepayment::create([
                'user_id'             => auth()->id(),
                'employee_advance_id' => $advance->id,
                'employee_id'         => $advance->employee_id,
                'user_account_id'     => $account->id,
                'repayment_date'      => $validated['repayment_date'],
                'amount'              => $amount,
                'source'              => 'manual',
                'payroll_id'          => null,
                'reference_no'        => $this->normalizeText($validated['reference_no'] ?? null),
                'note'                => $this->normalizeText($validated['note'] ?? null),
            ]);

            $this->recalculateAdvanceRepayment($advance);
            $this->syncRepaymentLedger($repayment, $advance->employee->name);
        });

        $message = __('Employee_Advance_Controller.advance_repayment_received_for_employee', [
            'name' => $advance->employee->name,
        ]);

        return redirect()
            ->route('user.hr.advances.repayments.index', $advance->id)
            ->with('toast_success', $message)
            ->with('ok', $message);
    }

    public function repaymentEdit($advanceId, $repaymentId)
    {
        $advance = EmployeeAdvance::where('user_id', auth()->id())
            ->with('employee')
            ->findOrFail($advanceId);

        $repayment = EmployeeAdvanceRepayment::where('user_id', auth()->id())
            ->where('employee_advance_id', $advance->id)
            ->findOrFail($repaymentId);

        if (($repayment->source ?? 'manual') === 'payroll_adjustment') {
            $message = __('Employee_Advance_Controller.payroll_adjusted_repayment_cannot_be_edited');

            return redirect()
                ->route('user.hr.advances.repayments.index', $advance->id)
                ->with('toast_error', $message)
                ->with('err', $message);
        }

        $accounts = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->orderBy('name')
            ->get();

        return view('user.hr.advances.repayments.edit', compact('advance', 'repayment', 'accounts'));
    }

    public function repaymentUpdate(Request $request, $advanceId, $repaymentId)
    {
        $advance = EmployeeAdvance::where('user_id', auth()->id())
            ->with('employee')
            ->findOrFail($advanceId);

        $repayment = EmployeeAdvanceRepayment::where('user_id', auth()->id())
            ->where('employee_advance_id', $advance->id)
            ->findOrFail($repaymentId);

        if (($repayment->source ?? 'manual') === 'payroll_adjustment') {
            $message = __('Employee_Advance_Controller.payroll_adjusted_repayment_cannot_be_updated');

            return redirect()
                ->route('user.hr.advances.repayments.index', $advance->id)
                ->with('toast_error', $message)
                ->with('err', $message);
        }

        $validated = $request->validate([
            'user_account_id' => ['required', 'exists:user_accounts,id'],
            'repayment_date'  => ['required', 'date'],
            'amount'          => ['required', 'numeric', 'min:0.01'],
            'reference_no'    => ['nullable', 'string', 'max:255'],
            'note'            => ['nullable', 'string'],
        ]);

        $account = UserAccount::where('user_id', auth()->id())
            ->where('status', 'active')
            ->findOrFail($validated['user_account_id']);

        $newAmount = round((float) $validated['amount'], 2);

        $otherRepaymentsTotal = EmployeeAdvanceRepayment::where('user_id', auth()->id())
            ->where('employee_advance_id', $advance->id)
            ->where('id', '!=', $repayment->id)
            ->sum('amount');

        if (($otherRepaymentsTotal + $newAmount) > (float) $advance->amount) {
            return back()
                ->withErrors([
                    'amount' => __('Employee_Advance_Controller.total_repayment_cannot_exceed_advance_amount'),
                ])
                ->withInput();
        }

        DB::transaction(function () use ($advance, $repayment, $account, $validated, $newAmount) {
            $this->deleteRepaymentLedger($repayment);

            $repayment->update([
                'user_account_id' => $account->id,
                'repayment_date'  => $validated['repayment_date'],
                'amount'          => $newAmount,
                'source'          => 'manual',
                'payroll_id'      => null,
                'reference_no'    => $this->normalizeText($validated['reference_no'] ?? null),
                'note'            => $this->normalizeText($validated['note'] ?? null),
            ]);

            $this->recalculateAdvanceRepayment($advance);
            $this->syncRepaymentLedger($repayment, $advance->employee->name, true);
        });

        $message = __('Employee_Advance_Controller.advance_repayment_updated_for_employee', [
            'name' => $advance->employee->name,
        ]);

        return redirect()
            ->route('user.hr.advances.repayments.index', $advance->id)
            ->with('toast_success', $message)
            ->with('ok', $message);
    }

    public function repaymentDestroy($advanceId, $repaymentId)
    {
        $advance = EmployeeAdvance::where('user_id', auth()->id())
            ->with('employee')
            ->findOrFail($advanceId);

        $repayment = EmployeeAdvanceRepayment::where('user_id', auth()->id())
            ->where('employee_advance_id', $advance->id)
            ->findOrFail($repaymentId);

        if (($repayment->source ?? 'manual') === 'payroll_adjustment') {
            $message = __('Employee_Advance_Controller.payroll_adjusted_repayment_cannot_be_deleted');

            return redirect()
                ->route('user.hr.advances.repayments.index', $advance->id)
                ->with('toast_error', $message)
                ->with('err', $message);
        }

        DB::transaction(function () use ($advance, $repayment) {
            $this->deleteRepaymentLedger($repayment);
            $repayment->delete();
            $this->recalculateAdvanceRepayment($advance);
        });

        $message = __('Employee_Advance_Controller.advance_repayment_deleted_for_employee', [
            'name' => $advance->employee->name,
        ]);

        return redirect()
            ->route('user.hr.advances.repayments.index', $advance->id)
            ->with('toast_success', $message)
            ->with('ok', $message);
    }

    private function recalculateAdvanceRepayment(EmployeeAdvance $advance): void
    {
        $paid = round((float) EmployeeAdvanceRepayment::where('user_id', auth()->id())
            ->where('employee_advance_id', $advance->id)
            ->sum('amount'), 2);

        $remaining = round((float) $advance->amount - $paid, 2);

        $status = 'active';

        if ($paid > 0 && $remaining > 0) {
            $status = 'partial_adjusted';
        } elseif ($remaining <= 0) {
            $status = 'adjusted';
            $remaining = 0;
        }

        $advance->update([
            'adjusted_amount'  => $paid,
            'remaining_amount' => $remaining,
            'status'           => $status,
        ]);
    }

    private function syncLedgers(EmployeeAdvance $advance, string $employeeName, bool $isUpdate = false): void
    {
        AccountLedgerService::post(
            auth()->id(),
            $advance->user_account_id,
            'employee_advance',
            'employee_advance',
            $advance->id,
            0,
            (float) $advance->amount,
            $isUpdate
                ? __('Employee_Advance_Controller.employee_advance_updated_with_name') . ': ' . $employeeName
                : __('Employee_Advance_Controller.employee_advance_paid_with_name') . ': ' . $employeeName,
            $advance->reference_no,
            $advance->advance_date
        );
    }

    private function syncRepaymentLedger(EmployeeAdvanceRepayment $repayment, string $employeeName, bool $isUpdate = false): void
    {
        AccountLedgerService::post(
            auth()->id(),
            $repayment->user_account_id,
            'employee_advance_repayment',
            EmployeeAdvanceRepayment::class,
            $repayment->id,
            (float) $repayment->amount,
            0,
            $isUpdate
                ? __('Employee_Advance_Controller.employee_advance_repayment_updated_with_name') . ': ' . $employeeName
                : __('Employee_Advance_Controller.employee_advance_repayment_received_with_name') . ': ' . $employeeName,
            $repayment->reference_no,
            $repayment->repayment_date
        );
    }

    private function deleteLedgers(EmployeeAdvance $advance): void
    {
        AccountLedgerService::deleteByReference(
            auth()->id(),
            'employee_advance',
            $advance->id
        );
    }

    private function deleteRepaymentLedger(EmployeeAdvanceRepayment $repayment): void
    {
        AccountLedgerService::deleteByReference(
            auth()->id(),
            'employee_advance_repayment',
            $repayment->id
        );
    }

    private function normalizeText(?string $value): ?string
    {
        $value = is_null($value) ? null : trim($value);

        return $value !== '' ? $value : null;
    }
}