<?php

namespace App\Http\Controllers\User;

use App\Http\Controllers\Controller;
use App\Models\Employee;
use App\Models\EmployeeAdvance;
use App\Models\EmployeeAttendance;
use App\Models\Payroll;
use App\Services\PayrollService;
use Carbon\Carbon;
use Illuminate\Http\Request;

class PayrollController extends Controller
{
    public function index()
    {
        $payrolls = Payroll::where('user_id', auth()->id())
            ->withCount('items')
            ->orderByDesc('payroll_year')
            ->orderByDesc('payroll_month')
            ->paginate(12);

        return view('user.hr.payrolls.index', compact('payrolls'));
    }

    public function create()
    {
        return view('user.hr.payrolls.create');
    }

    public function preview(Request $request)
    {
        $validated = $request->validate([
            'payroll_month' => ['required', 'integer', 'min:1', 'max:12'],
            'payroll_year'  => ['required', 'integer', 'min:2000', 'max:2100'],
            'payroll_date'  => ['nullable', 'date'],
        ]);

        $userId = auth()->id();
        $month  = (int) $validated['payroll_month'];
        $year   = (int) $validated['payroll_year'];

        $alreadyFinalized = Payroll::query()
            ->where('user_id', $userId)
            ->where('payroll_month', $month)
            ->where('payroll_year', $year)
            ->whereIn('status', ['finalized', 'paid_partial', 'paid_full'])
            ->exists();

        if ($alreadyFinalized) {
            $message = __('PayrollController.payroll_already_finalized');

            return redirect()
                ->route('user.hr.payrolls.create')
                ->withInput()
                ->with('swal_error', $message);
        }

        $payrollDate = $validated['payroll_date'] ?? now()->format('Y-m-d');

        $startDate = Carbon::create($year, $month, 1)->startOfMonth()->toDateString();
        $endDate   = Carbon::create($year, $month, 1)->endOfMonth()->toDateString();

        $employees = Employee::query()
            ->where('user_id', $userId)
            ->where('status', 'active')
            ->with(['currentSalaryStructure', 'designation'])
            ->orderBy('name')
            ->get()
            ->map(function ($employee) use ($userId, $startDate, $endDate) {
                $structure = $employee->currentSalaryStructure;

                if (!$structure) {
                    return null;
                }

                $attendance = EmployeeAttendance::query()
                    ->where('user_id', $userId)
                    ->where('employee_id', $employee->id)
                    ->whereBetween('attendance_date', [$startDate, $endDate]);

                $present = (clone $attendance)->where('day_status', 'present')->count();
                $absent  = (clone $attendance)->where('day_status', 'absent')->count();
                $leave   = (clone $attendance)->where('day_status', 'leave')->count();
                $holiday = (clone $attendance)->where('day_status', 'holiday')->count();

                $otHours = (float) (clone $attendance)->sum('overtime_hours');
                $roz     = (float) (clone $attendance)->sum('roz_value');

                $monthly = money($structure->monthly_salary ?? 0);
                $perRoz  = money($structure->per_roz_rate ?? 0);
                $otRate  = money($structure->overtime_rate ?? 0);

                $perDay = 0;
                $absentDeduction = 0;
                $basic = 0;
                $otAmount = 0;

                if ($employee->employee_type === 'monthly') {
                    $days = $present + $absent + $leave;

                    if ($days > 0) {
                        $perDay = money($monthly / $days);
                        $absentDeduction = money($perDay * $absent);
                        $basic = money(max(0, $monthly - $absentDeduction));
                    } else {
                        $basic = 0;
                    }

                    $otAmount = money($employee->overtime_eligible ? ($otRate * $otHours) : 0);
                } else {
                    $otHours = 0;
                    $basic = money($perRoz * $roz);
                    $otAmount = 0;
                }

                $gross = money($basic + $otAmount);
                $preNet = money(max(0, $gross));

                $advance = money(
                    EmployeeAdvance::query()
                        ->where('user_id', $userId)
                        ->where('employee_id', $employee->id)
                        ->whereDate('advance_date', '<=', $endDate)
                        ->whereIn('status', ['active', 'partial_adjusted'])
                        ->where('remaining_amount', '>', 0)
                        ->sum('remaining_amount')
                );

                $maxDeduction = money(min($preNet, $advance));

                return (object) [
                    'employee'             => $employee,
                    'total_present_days'   => $present,
                    'total_absent_days'    => $absent,
                    'total_leave_days'     => $leave,
                    'total_holiday_days'   => $holiday,
                    'total_overtime_hours' => $otHours,
                    'total_roz'            => $roz,

                    'rate_monthly_salary'  => $monthly,
                    'rate_per_roz'         => $perRoz,
                    'rate_overtime_rate'   => $otRate,

                    'per_day_salary'       => $perDay,
                    'basic_amount'         => $basic,
                    'deduction_amount'     => money($absentDeduction),
                    'overtime_amount'      => $otAmount,
                    'gross_salary'         => $gross,
                    'pre_net_salary'       => $preNet,

                    'outstanding_advance'  => $advance,
                    'max_deduction'        => $maxDeduction,
                ];
            })
            ->filter()
            ->values();

        return view('user.hr.payrolls.preview', compact('employees', 'month', 'year', 'payrollDate'));
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'payroll_month'      => ['required', 'integer'],
            'payroll_year'       => ['required', 'integer'],
            'payroll_date'       => ['nullable', 'date'],
            'advance_deductions' => ['nullable', 'array'],
        ]);

        $alreadyFinalized = Payroll::query()
            ->where('user_id', auth()->id())
            ->where('payroll_month', (int) $validated['payroll_month'])
            ->where('payroll_year', (int) $validated['payroll_year'])
            ->whereIn('status', ['finalized', 'paid_partial', 'paid_full'])
            ->exists();

        if ($alreadyFinalized) {
            $message = __('PayrollController.payroll_already_finalized');

            return redirect()
                ->route('user.hr.payrolls.create')
                ->withInput()
                ->with('swal_error', $message);
        }

        try {
            $payroll = PayrollService::generate(
                userId: auth()->id(),
                month: (int) $validated['payroll_month'],
                year: (int) $validated['payroll_year'],
                payrollDate: $validated['payroll_date'] ?? null,
                advanceDeductions: $validated['advance_deductions'] ?? []
            );

            $message = __('PayrollController.payroll_generated_successfully');

            return redirect()
                ->route('user.hr.payrolls.show', $payroll->id)
                ->with('toast_success', $message)
                ->with('ok', $message);
        } catch (\Throwable $e) {
            return back()
                ->withInput()
                ->with('toast_error', $e->getMessage())
                ->with('err', $e->getMessage());
        }
    }

    public function show($id)
    {
        $payroll = Payroll::where('user_id', auth()->id())
            ->with(['items.employee.designation', 'payments.employee'])
            ->findOrFail($id);

        return view('user.hr.payrolls.show', compact('payroll'));
    }

    public function finalize($id)
    {
        $payroll = Payroll::where('user_id', auth()->id())->findOrFail($id);

        try {
            PayrollService::finalize($payroll);

            $message = __('PayrollController.payroll_finalized_successfully');

            return back()
                ->with('toast_success', $message)
                ->with('ok', $message);
        } catch (\Throwable $e) {
            return back()
                ->with('toast_error', $e->getMessage())
                ->with('err', $e->getMessage());
        }
    }

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

        try {
            PayrollService::delete($payroll);

            $message = __('PayrollController.payroll_deleted_successfully');

            return redirect()
                ->route('user.hr.payrolls.index')
                ->with('toast_success', $message)
                ->with('ok', $message);
        } catch (\Throwable $e) {
            return back()
                ->with('toast_error', $e->getMessage())
                ->with('err', $e->getMessage());
        }
    }

    public function checkFinalized(Request $request)
    {
        $validated = $request->validate([
            'payroll_month' => ['required', 'integer', 'min:1', 'max:12'],
            'payroll_year'  => ['required', 'integer', 'min:2000', 'max:2100'],
        ]);

        $alreadyFinalized = Payroll::query()
            ->where('user_id', auth()->id())
            ->where('payroll_month', (int) $validated['payroll_month'])
            ->where('payroll_year', (int) $validated['payroll_year'])
            ->whereIn('status', ['finalized', 'paid_partial', 'paid_full'])
            ->exists();

        return response()->json([
            'already_finalized' => $alreadyFinalized,
        ]);
    }
}