<?php

namespace App\Http\Controllers;

use App\Models\AccountTransfer;
use App\Models\AttendanceLog;
use App\Models\AttendanceSetting;
use App\Models\Employee;
use App\Models\EmployeeBonus;
use App\Models\EmployeeCommission;
use App\Models\EmployeePenalty;
use App\Models\EmployeeWithdrawal;
use App\Models\Expense;
use App\Models\ExpenseArchiveItem;
use App\Models\Leave;
use App\Models\MonthlySettlement;
use App\Models\OperationsTask;
use App\Models\ReserveBox;
use App\Models\ReserveBoxTransaction;
use App\Models\ServiceSubscription;
use App\Models\StageAcademyActivation;
use App\Models\SubscriptionPayment;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;

class AccountsController extends Controller
{
    public function __construct()
    {
        $this->middleware('permission:accounts.view')->only(['index', 'settlements', 'expenses', 'salaries', 'employeeAccounts', 'employeeAccountsPrint', 'employeeAccountsCommissions']);

        $this->middleware('permission:accounts.employee_withdrawals.view')->only(['employeeWithdrawals']);
        $this->middleware('permission:accounts.employee_withdrawals.create')->only(['employeeWithdrawalsStore']);
        $this->middleware('permission:accounts.employee_withdrawals.update')->only(['employeeWithdrawalsUpdate']);
        $this->middleware('permission:accounts.employee_withdrawals.delete')->only(['employeeWithdrawalsDestroy']);

        $this->middleware('permission:accounts.penalties.view')->only(['employeePenalties', 'penaltiesIndex']);
        $this->middleware('permission:accounts.penalties.create')->only(['penaltiesStore']);
        $this->middleware('permission:accounts.penalties.update')->only(['penaltiesUpdate']);
        $this->middleware('permission:accounts.penalties.delete')->only(['penaltiesDestroy']);

        $this->middleware('permission:accounts.employee_bonuses.view')->only(['employeeBonuses']);
        $this->middleware('permission:accounts.employee_bonuses.create')->only(['employeeBonusesStore']);
        $this->middleware('permission:accounts.employee_bonuses.update')->only(['employeeBonusesUpdate']);
        $this->middleware('permission:accounts.employee_bonuses.delete')->only(['employeeBonusesDestroy']);

        $this->middleware('permission:accounts.boxes.view')->only(['boxes']);

        $this->middleware('permission:accounts.reserve_box.view')->only([
            'reserveBox',
            'reserveBoxCompensationReceipt',
            'reserveBoxTransferToOwnerReceipt',
            'partnersTransferReceipt',
        ]);
        $this->middleware('permission:accounts.reserve_box.update')->only([
            'reserveBoxTransferToOwner',
            'reserveBoxTransferBetweenBoxes',
            'reserveBoxCompensation',
        ]);

        $this->middleware('permission:accounts.partners.view')->only(['partners']);

        $this->middleware('permission:accounts.reports.view')->only(['reports']);

        $this->middleware('permission:accounts.attendance.view')->only(['attendance']);
        $this->middleware('permission:accounts.attendance.today.view')->only(['attendanceToday']);
        $this->middleware('permission:accounts.attendance.absences.view')->only(['attendanceAbsences']);
        $this->middleware('permission:accounts.attendance.time_records.view')->only(['attendanceTimeRecords']);
        $this->middleware('permission:accounts.attendance.leaves.view')->only(['attendanceLeaves']);
        $this->middleware('permission:accounts.attendance.leaves.update')->only([
            'approveTimedLeave',
            'rejectTimedLeave',
            'approveLeave',
            'rejectLeave',
        ]);
        $this->middleware('permission:accounts.attendance.reports.view')->only(['attendanceReports']);

        $this->middleware('permission:accounts.cash_box.view')->only(['cashBox']);
        $this->middleware('permission:accounts.online_box.view')->only(['onlineBox']);

        $this->middleware('permission:accounts.debts.view')->only(['debts']);
        $this->middleware('permission:accounts.debts.payments.view')->only(['debtPayments']);
        $this->middleware('permission:accounts.debts.invoice.view')->only(['debtInvoice']);
        $this->middleware('permission:accounts.debts.payments.create')->only(['storeDebtPayment']);
        $this->middleware('permission:accounts.debts.payments.update')->only(['updateDebtPayment']);
        $this->middleware('permission:accounts.debts.payments.delete')->only(['destroyDebtPayment']);

        $this->middleware('permission:accounts.transfers.view')->only(['transfers']);
        $this->middleware('permission:accounts.transfers.create')->only(['storeTransfer']);
        $this->middleware('permission:accounts.transfers.update')->only(['updateTransfer']);
        $this->middleware('permission:accounts.transfers.delete')->only(['destroyTransfer']);
    }

    public function index()
    {
        return view('accounts.index');
    }

    public function settlements()
    {
        return view('accounts.settlements');
    }

    public function expenses()
    {
        return view('accounts.expenses');
    }

    public function salaries()
    {
        return view('accounts.salaries');
    }

    public function employeeAccounts()
    {
        $tz = 'Asia/Baghdad';
        $nowLocal = Carbon::now($tz);
        $lastSettlement = MonthlySettlement::query()
            ->orderByDesc('settled_at')
            ->orderByDesc('date_to')
            ->orderByDesc('id')
            ->first();

        $periodFromLocal = $lastSettlement?->settled_at
            ? $lastSettlement->settled_at->copy()->setTimezone($tz)->addSecond()
            : ($lastSettlement?->date_to
                ? $lastSettlement->date_to->copy()->addDay()->startOfDay()
                : $nowLocal->copy()->startOfMonth());

        $periodToLocal = $nowLocal->copy()->endOfDay();
        $fromUtc = $periodFromLocal->copy()->setTimezone('UTC');
        $toUtc = $periodToLocal->copy()->setTimezone('UTC');

        $employees = Employee::query()
            ->where('is_active', true)
            ->orderBy('full_name')
            ->get(['id', 'full_name', 'base_salary', 'general_percentage', 'role_percentage', 'avatar_path']);

        $bonusesTotals = EmployeeBonus::query()
            ->selectRaw('employee_id')
            ->selectRaw('COALESCE(SUM(amount), 0) as total')
            ->whereBetween('created_at', [$fromUtc, $toUtc])
            ->groupBy('employee_id')
            ->pluck('total', 'employee_id');

        $penaltiesTotals = EmployeePenalty::query()
            ->selectRaw('employee_id')
            ->selectRaw('COALESCE(SUM(amount), 0) as total')
            ->whereBetween('created_at', [$fromUtc, $toUtc])
            ->groupBy('employee_id')
            ->pluck('total', 'employee_id');

        $withdrawalsTotals = EmployeeWithdrawal::query()
            ->selectRaw('employee_id')
            ->selectRaw('COALESCE(SUM(amount), 0) as total')
            ->whereBetween('created_at', [$fromUtc, $toUtc])
            ->groupBy('employee_id')
            ->pluck('total', 'employee_id');

        $absentDaysTotals = AttendanceLog::query()
            ->selectRaw('employee_id')
            ->selectRaw('COUNT(*) as total')
            ->where('type', 'check_in')
            ->where('attendance_status', 'absent')
            ->whereBetween('logged_at', [$fromUtc, $toUtc])
            ->groupBy('employee_id')
            ->pluck('total', 'employee_id');

        $settings = AttendanceSetting::query()->first();
        $absenceDeductionDays = max(0, min(30, (int) ($settings?->absence_deduction_days ?? 2)));
        $unpaidLeaveDeductionDays = max(0, min(30, (int) ($settings?->unpaid_leave_deduction_days ?? 1)));
        $lateMinutePrice = max(0, (int) ($settings?->late_minute_price ?? 0));

        $lateMinutesTotals = AttendanceLog::query()
            ->selectRaw('employee_id')
            ->selectRaw("COALESCE(SUM(CASE WHEN attendance_status = 'late' THEN late_minutes ELSE 0 END), 0) as total")
            ->where('type', 'check_in')
            ->whereBetween('logged_at', [$fromUtc, $toUtc])
            ->groupBy('employee_id')
            ->pluck('total', 'employee_id');

        $checkInDatesByEmployee = AttendanceLog::query()
            ->where('type', 'check_in')
            ->whereBetween('logged_at', [$fromUtc, $toUtc])
            ->get(['employee_id', 'logged_at'])
            ->groupBy('employee_id')
            ->map(function ($items) use ($tz) {
                return $items
                    ->map(fn ($log) => $log->logged_at?->timezone($tz)?->toDateString())
                    ->filter()
                    ->unique()
                    ->values()
                    ->all();
            });

        $fullDayLeaveDatesByEmployee = Leave::query()
            ->where('leave_type', 'full_day')
            ->where('status', 'approved')
            ->whereBetween('leave_date', [$periodFromLocal->toDateString(), $periodToLocal->toDateString()])
            ->get(['employee_id', 'leave_date'])
            ->groupBy('employee_id')
            ->map(function ($items) {
                return $items
                    ->map(fn ($leave) => $leave->leave_date?->toDateString())
                    ->filter()
                    ->unique()
                    ->values()
                    ->all();
            });

        $unpaidLeavesTotals = Leave::query()
            ->selectRaw('employee_id')
            ->selectRaw('COUNT(*) as total')
            ->where('leave_type', 'full_day')
            ->where('status', 'approved')
            ->where('is_paid', false)
            ->whereBetween('leave_date', [$periodFromLocal->toDateString(), $periodToLocal->toDateString()])
            ->groupBy('employee_id')
            ->pluck('total', 'employee_id');

        $workDates = [];
        $cursor = $periodFromLocal->copy()->startOfDay();
        $workEnd = $periodToLocal->copy()->startOfDay();
        while ($cursor->lessThanOrEqualTo($workEnd)) {
            if (! $cursor->isFriday()) {
                $workDates[] = $cursor->toDateString();
            }
            $cursor->addDay();
        }

        $commissionsAgg = EmployeeCommission::query()
            ->selectRaw('employee_id')
            ->selectRaw('COALESCE(SUM(commission_amount), 0) as total')
            ->selectRaw('COALESCE(SUM(base_amount), 0) as base_total')
            ->where('status', 'pending')
            ->whereBetween('eligible_at', [$fromUtc, $toUtc])
            ->groupBy('employee_id')
            ->get()
            ->keyBy('employee_id');

        $commissions = EmployeeCommission::query()
            ->where('status', 'pending')
            ->whereBetween('eligible_at', [$fromUtc, $toUtc])
            ->orderByDesc('eligible_at')
            ->orderByDesc('id')
            ->get([
                'id',
                'employee_id',
                'service_key',
                'commission_type',
                'base_amount',
                'percentage',
                'commission_amount',
                'source_type',
                'source_id',
                'eligible_at',
            ]);

        $subscriptionIds = $commissions
            ->where('source_type', 'service_subscription')
            ->pluck('source_id')
            ->filter()
            ->unique()
            ->values();

        $taskIds = $commissions
            ->where('source_type', 'operations_task')
            ->pluck('source_id')
            ->filter()
            ->unique()
            ->values();

        $subscriptionsById = ServiceSubscription::query()
            ->with(['customer:id,name', 'service:id,type'])
            ->whereIn('id', $subscriptionIds)
            ->get(['id', 'customer_id', 'service_id', 'invoice_number'])
            ->keyBy('id');

        $tasksById = OperationsTask::query()
            ->with(['item.order.subscription.customer:id,name', 'item.order.subscription.service:id,type'])
            ->whereIn('id', $taskIds)
            ->get(['id', 'order_item_id', 'title'])
            ->keyBy('id');

        $commissionsByEmployeeId = $commissions
            ->groupBy('employee_id')
            ->map(function ($items) use ($subscriptionsById, $tasksById) {
                return $items->map(function (EmployeeCommission $c) use ($subscriptionsById, $tasksById) {
                    $serviceLabel = __($c->service_key);
                    if ($serviceLabel === $c->service_key) {
                        $serviceLabel = $c->service_key;
                    }

                    $typeLabel = $c->commission_type === 'sales'
                        ? __('accounts.employee_accounts.commission_types.sales')
                        : ($c->commission_type === 'work'
                            ? __('accounts.employee_accounts.commission_types.work')
                            : $c->commission_type);

                    $sourceLabel = '—';
                    if ($c->source_type === 'service_subscription' && $c->source_id) {
                        $sub = $subscriptionsById->get($c->source_id);
                        if ($sub) {
                            $sourceLabel = trim(($sub->invoice_number ?: '').' · '.($sub->customer?->name ?: ''));
                            $sourceLabel = $sourceLabel !== '·' ? $sourceLabel : '—';
                        }
                    } elseif ($c->source_type === 'operations_task' && $c->source_id) {
                        $task = $tasksById->get($c->source_id);
                        if ($task) {
                            $sub = $task->item?->order?->subscription;
                            $subPart = $sub ? trim(($sub->invoice_number ?: '').' · '.($sub->customer?->name ?: '')) : '';
                            $sourceLabel = $subPart !== '' ? $task->title.' · '.$subPart : ($task->title ?: '—');
                        }
                    }

                    return [
                        'id' => $c->id,
                        'eligible_at' => $c->eligible_at,
                        'service_label' => $serviceLabel,
                        'type_label' => $typeLabel,
                        'source_label' => $sourceLabel,
                        'base_amount' => (int) $c->base_amount,
                        'percentage' => (int) $c->percentage,
                        'commission_amount' => (int) $c->commission_amount,
                    ];
                });
            });

        $rows = $employees->map(function (Employee $employee) use ($tz, $workDates, $bonusesTotals, $penaltiesTotals, $withdrawalsTotals, $absentDaysTotals, $lateMinutesTotals, $checkInDatesByEmployee, $fullDayLeaveDatesByEmployee, $unpaidLeavesTotals, $absenceDeductionDays, $unpaidLeaveDeductionDays, $lateMinutePrice, $commissionsAgg, $commissionsByEmployeeId) {
            $bonus = (int) ($bonusesTotals->get($employee->id, 0) ?? 0);
            $penalty = (int) ($penaltiesTotals->get($employee->id, 0) ?? 0);
            $withdrawalsTotal = (int) ($withdrawalsTotals->get($employee->id, 0) ?? 0);
            $absentStatusDaysTotal = (int) ($absentDaysTotals->get($employee->id, 0) ?? 0);
            $lateMinutesTotal = (int) ($lateMinutesTotals->get($employee->id, 0) ?? 0);
            $unpaidLeavesTotal = (int) ($unpaidLeavesTotals->get($employee->id, 0) ?? 0);

            $checkInDates = $checkInDatesByEmployee->get($employee->id, []);
            $leaveDates = $fullDayLeaveDatesByEmployee->get($employee->id, []);
            $presentOrExcusedDates = array_flip(array_merge($checkInDates, $leaveDates));

            $noShowAbsentDays = 0;
            foreach ($workDates as $d) {
                if (! isset($presentOrExcusedDates[$d])) {
                    $noShowAbsentDays++;
                }
            }

            $absentDaysTotal = $absentStatusDaysTotal + $noShowAbsentDays;
            $commissionRow = $commissionsAgg->get($employee->id);
            $commissionTotal = (int) ($commissionRow?->total ?? 0);
            $commissionBaseTotal = (int) ($commissionRow?->base_total ?? 0);
            $effectiveCommissionPercent = $commissionBaseTotal > 0
                ? round(($commissionTotal / $commissionBaseTotal) * 100, 2)
                : 0;

            $baseSalary = (int) ($employee->base_salary ?? 0);
            $dailyRate = $baseSalary > 0 ? ($baseSalary / \App\Models\Employee::DEFAULT_SALARY_WORKING_DAYS_DIVISOR) : 0;

            $absencesAmount = (int) round($absentDaysTotal * $absenceDeductionDays * $dailyRate);
            $unpaidLeaveAmount = (int) round($unpaidLeavesTotal * $unpaidLeaveDeductionDays * $dailyRate);

            $lateAmount = 0;
            if ($lateMinutesTotal > 0) {
                if ($lateMinutePrice > 0) {
                    $lateAmount = $lateMinutesTotal * $lateMinutePrice;
                } else {
                    $lateAmount = (int) round($lateMinutesTotal * $employee->minuteRate(\App\Models\Employee::DEFAULT_SALARY_WORKING_DAYS_DIVISOR, $tz));
                }
            }

            $attendanceDeductionsTotal = (int) ($absencesAmount + $unpaidLeaveAmount + $lateAmount);

            $net = $baseSalary
                + $commissionTotal
                + $bonus
                - $penalty
                - $withdrawalsTotal
                - $attendanceDeductionsTotal;

            return [
                'employee' => $employee,
                'base_salary' => $baseSalary,
                'bonuses_total' => $bonus,
                'penalties_total' => $penalty,
                'withdrawals_total' => $withdrawalsTotal,
                'absences_total' => $attendanceDeductionsTotal,
                'commissions_total' => $commissionTotal,
                'commissions_base_total' => $commissionBaseTotal,
                'commissions_effective_percent' => $effectiveCommissionPercent,
                'net_total' => $net,
                'commissions' => $commissionsByEmployeeId->get($employee->id, collect()),
            ];
        });

        return view('accounts.employee_accounts', [
            'title' => __('accounts.employee_accounts.title'),
            'subtitle' => __('accounts.employee_accounts.subtitle'),
            'period_from' => $periodFromLocal,
            'period_to' => $periodToLocal,
            'rows' => $rows,
        ]);
    }

    public function employeeAccountsPrint(Employee $employee)
    {
        $tz = 'Asia/Baghdad';
        $nowLocal = Carbon::now($tz);
        $lastSettlement = MonthlySettlement::query()
            ->orderByDesc('settled_at')
            ->orderByDesc('date_to')
            ->orderByDesc('id')
            ->first();

        $periodFromLocal = $lastSettlement?->settled_at
            ? $lastSettlement->settled_at->copy()->setTimezone($tz)->addSecond()
            : ($lastSettlement?->date_to
                ? $lastSettlement->date_to->copy()->addDay()->startOfDay()
                : $nowLocal->copy()->startOfMonth());

        $periodToLocal = $nowLocal->copy()->endOfDay();
        $fromUtc = $periodFromLocal->copy()->setTimezone('UTC');
        $toUtc = $periodToLocal->copy()->setTimezone('UTC');

        $commissions = EmployeeCommission::query()
            ->where('employee_id', $employee->id)
            ->where('status', 'pending')
            ->whereBetween('eligible_at', [$fromUtc, $toUtc])
            ->orderByDesc('eligible_at')
            ->orderByDesc('id')
            ->get();

        $bonuses = EmployeeBonus::query()
            ->with('creator')
            ->where('employee_id', $employee->id)
            ->whereBetween('created_at', [$fromUtc, $toUtc])
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->get();

        $penalties = EmployeePenalty::query()
            ->with('creator')
            ->where('employee_id', $employee->id)
            ->whereBetween('created_at', [$fromUtc, $toUtc])
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->get();

        $withdrawalsTotal = (int) EmployeeWithdrawal::query()
            ->where('employee_id', $employee->id)
            ->whereBetween('created_at', [$fromUtc, $toUtc])
            ->sum('amount');

        $absentDaysTotal = (int) AttendanceLog::query()
            ->where('employee_id', $employee->id)
            ->where('type', 'check_in')
            ->where('attendance_status', 'absent')
            ->whereBetween('logged_at', [$fromUtc, $toUtc])
            ->count();

        $settings = AttendanceSetting::query()->first();
        $absenceDeductionDays = max(0, min(30, (int) ($settings?->absence_deduction_days ?? 2)));
        $unpaidLeaveDeductionDays = max(0, min(30, (int) ($settings?->unpaid_leave_deduction_days ?? 1)));
        $lateMinutePrice = max(0, (int) ($settings?->late_minute_price ?? 0));

        $baseSalary = (int) ($employee->base_salary ?? 0);
        $commissionsTotal = (int) $commissions->sum('commission_amount');
        $commissionsBaseTotal = (int) $commissions->sum('base_amount');
        $bonusesTotal = (int) $bonuses->sum('amount');
        $penaltiesTotal = (int) $penalties->sum('amount');
        $effectiveCommissionPercent = $commissionsBaseTotal > 0
            ? round(($commissionsTotal / $commissionsBaseTotal) * 100, 2)
            : 0;

        $lateMinutesTotal = (int) AttendanceLog::query()
            ->where('employee_id', $employee->id)
            ->where('type', 'check_in')
            ->where('attendance_status', 'late')
            ->whereBetween('logged_at', [$fromUtc, $toUtc])
            ->sum('late_minutes');

        $checkInDates = AttendanceLog::query()
            ->where('employee_id', $employee->id)
            ->where('type', 'check_in')
            ->whereBetween('logged_at', [$fromUtc, $toUtc])
            ->get(['logged_at'])
            ->map(fn ($log) => $log->logged_at?->timezone($tz)?->toDateString())
            ->filter()
            ->unique()
            ->values()
            ->all();

        $leaveDates = Leave::query()
            ->where('employee_id', $employee->id)
            ->where('leave_type', 'full_day')
            ->where('status', 'approved')
            ->whereBetween('leave_date', [$periodFromLocal->toDateString(), $periodToLocal->toDateString()])
            ->get(['leave_date'])
            ->map(fn ($leave) => $leave->leave_date?->toDateString())
            ->filter()
            ->unique()
            ->values()
            ->all();

        $presentOrExcusedDates = array_flip(array_merge($checkInDates, $leaveDates));

        $workDates = [];
        $cursor = $periodFromLocal->copy()->startOfDay();
        $workEnd = $periodToLocal->copy()->startOfDay();
        while ($cursor->lessThanOrEqualTo($workEnd)) {
            if (! $cursor->isFriday()) {
                $workDates[] = $cursor->toDateString();
            }
            $cursor->addDay();
        }

        $noShowAbsentDays = 0;
        foreach ($workDates as $d) {
            if (! isset($presentOrExcusedDates[$d])) {
                $noShowAbsentDays++;
            }
        }

        $absentDaysTotal += $noShowAbsentDays;

        $unpaidLeavesTotal = (int) Leave::query()
            ->where('employee_id', $employee->id)
            ->where('leave_type', 'full_day')
            ->where('status', 'approved')
            ->where('is_paid', false)
            ->whereBetween('leave_date', [$periodFromLocal->toDateString(), $periodToLocal->toDateString()])
            ->count();

        $dailyRate = $baseSalary > 0 ? ($baseSalary / \App\Models\Employee::DEFAULT_SALARY_WORKING_DAYS_DIVISOR) : 0;

        $absencesAmount = (int) round($absentDaysTotal * $absenceDeductionDays * $dailyRate);
        $unpaidLeaveAmount = (int) round($unpaidLeavesTotal * $unpaidLeaveDeductionDays * $dailyRate);

        $lateAmount = 0;
        if ($lateMinutesTotal > 0) {
            if ($lateMinutePrice > 0) {
                $lateAmount = $lateMinutesTotal * $lateMinutePrice;
            } else {
                $lateAmount = (int) round($lateMinutesTotal * $employee->minuteRate(\App\Models\Employee::DEFAULT_SALARY_WORKING_DAYS_DIVISOR, $tz));
            }
        }

        $attendanceDeductionsTotal = (int) ($absencesAmount + $unpaidLeaveAmount + $lateAmount);

        $netTotal = $baseSalary
            + $commissionsTotal
            + $bonusesTotal
            - $penaltiesTotal
            - $withdrawalsTotal
            - $attendanceDeductionsTotal;

        return view('accounts.employee_accounts_print', [
            'employee' => $employee,
            'period_from' => $periodFromLocal,
            'period_to' => $periodToLocal,
            'base_salary' => $baseSalary,
            'commissions_total' => $commissionsTotal,
            'commissions_effective_percent' => $effectiveCommissionPercent,
            'bonuses_total' => $bonusesTotal,
            'penalties_total' => $penaltiesTotal,
            'withdrawals_total' => $withdrawalsTotal,
            'absent_days_total' => $absentDaysTotal,
            'absence_deduction_days' => $absenceDeductionDays,
            'absences_total' => $attendanceDeductionsTotal,
            'net_total' => $netTotal,
            'printed_by' => Auth::user(),
        ]);
    }

    public function employeeAccountsCommissions(Employee $employee)
    {
        $tz = 'Asia/Baghdad';
        $nowLocal = Carbon::now($tz);
        $lastSettlement = MonthlySettlement::query()
            ->orderByDesc('settled_at')
            ->orderByDesc('date_to')
            ->orderByDesc('id')
            ->first();

        $periodFromLocal = $lastSettlement?->settled_at
            ? $lastSettlement->settled_at->copy()->setTimezone($tz)->addSecond()
            : ($lastSettlement?->date_to
                ? $lastSettlement->date_to->copy()->addDay()->startOfDay()
                : $nowLocal->copy()->startOfMonth());

        $periodToLocal = $nowLocal->copy()->endOfDay();
        $fromUtc = $periodFromLocal->copy()->setTimezone('UTC');
        $toUtc = $periodToLocal->copy()->setTimezone('UTC');

        $commissions = EmployeeCommission::query()
            ->where('employee_id', $employee->id)
            ->where('status', 'pending')
            ->whereBetween('eligible_at', [$fromUtc, $toUtc])
            ->orderByDesc('eligible_at')
            ->orderByDesc('id')
            ->paginate(50, [
                'id',
                'employee_id',
                'service_key',
                'commission_type',
                'base_amount',
                'percentage',
                'commission_amount',
                'source_type',
                'source_id',
                'eligible_at',
            ]);

        $pageItems = $commissions->getCollection();
        $subscriptionIds = $pageItems
            ->where('source_type', 'service_subscription')
            ->pluck('source_id')
            ->filter()
            ->unique()
            ->values();

        $taskIds = $pageItems
            ->where('source_type', 'operations_task')
            ->pluck('source_id')
            ->filter()
            ->unique()
            ->values();

        $subscriptionsById = ServiceSubscription::query()
            ->with(['customer:id,name', 'service:id,type'])
            ->whereIn('id', $subscriptionIds)
            ->get(['id', 'customer_id', 'service_id', 'invoice_number'])
            ->keyBy('id');

        $tasksById = OperationsTask::query()
            ->with(['item.order.subscription.customer:id,name', 'item.order.subscription.service:id,type'])
            ->whereIn('id', $taskIds)
            ->get(['id', 'order_item_id', 'title'])
            ->keyBy('id');

        $commissions->setCollection(
            $pageItems->map(function (EmployeeCommission $c) use ($subscriptionsById, $tasksById) {
                $serviceLabel = __($c->service_key);
                if ($serviceLabel === $c->service_key) {
                    $serviceLabel = $c->service_key;
                }

                $typeLabel = $c->commission_type === 'sales'
                    ? __('accounts.employee_accounts.commission_types.sales')
                    : ($c->commission_type === 'work'
                        ? __('accounts.employee_accounts.commission_types.work')
                        : $c->commission_type);

                $sourceLabel = '—';
                if ($c->source_type === 'service_subscription' && $c->source_id) {
                    $sub = $subscriptionsById->get($c->source_id);
                    if ($sub) {
                        $sourceLabel = trim(($sub->invoice_number ?: '').' · '.($sub->customer?->name ?: ''));
                        $sourceLabel = $sourceLabel !== '·' ? $sourceLabel : '—';
                    }
                } elseif ($c->source_type === 'operations_task' && $c->source_id) {
                    $task = $tasksById->get($c->source_id);
                    if ($task) {
                        $sub = $task->item?->order?->subscription;
                        $subPart = $sub ? trim(($sub->invoice_number ?: '').' · '.($sub->customer?->name ?: '')) : '';
                        $sourceLabel = $subPart !== '' ? $task->title.' · '.$subPart : ($task->title ?: '—');
                    }
                }

                return [
                    'id' => $c->id,
                    'eligible_at' => $c->eligible_at,
                    'service_label' => $serviceLabel,
                    'type_label' => $typeLabel,
                    'source_label' => $sourceLabel,
                    'base_amount' => (int) $c->base_amount,
                    'percentage' => (int) $c->percentage,
                    'commission_amount' => (int) $c->commission_amount,
                ];
            })
        );

        $commissionsTotal = (int) EmployeeCommission::query()
            ->where('employee_id', $employee->id)
            ->where('status', 'pending')
            ->whereBetween('eligible_at', [$fromUtc, $toUtc])
            ->sum('commission_amount');

        return view('accounts.employee_accounts_commissions', [
            'title' => __('accounts.employee_accounts.commissions_page_title'),
            'employee' => $employee,
            'period_from' => $periodFromLocal,
            'period_to' => $periodToLocal,
            'commissions_total' => $commissionsTotal,
            'commissions' => $commissions,
        ]);
    }

    public function employeeWithdrawals()
    {
        $employees = Employee::query()
            ->where('is_active', true)
            ->orderBy('full_name')
            ->get(['id', 'full_name']);

        $withdrawals = EmployeeWithdrawal::query()
            ->with(['employee', 'creator'])
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->get();

        return view('accounts.employee_withdrawals', [
            'title' => __('accounts.employee_withdrawals.title'),
            'employees' => $employees,
            'withdrawals' => $withdrawals,
        ]);
    }

    public function employeeWithdrawalsStore(Request $request)
    {
        $validated = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'amount' => 'required|integer|min:1',
            'reason' => 'required|string',
        ]);

        EmployeeWithdrawal::query()->create([
            'employee_id' => (int) $validated['employee_id'],
            'amount' => (int) $validated['amount'],
            'reason' => $validated['reason'],
            'created_by' => (int) Auth::id(),
        ]);

        return back()->with('success', __('accounts.employee_withdrawals.messages.created'));
    }

    public function employeeWithdrawalsUpdate(Request $request, int $id)
    {
        $withdrawal = EmployeeWithdrawal::query()->findOrFail($id);

        $validated = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'amount' => 'required|integer|min:1',
            'reason' => 'required|string',
        ]);

        $withdrawal->forceFill([
            'employee_id' => (int) $validated['employee_id'],
            'amount' => (int) $validated['amount'],
            'reason' => $validated['reason'],
        ])->save();

        return back()->with('success', __('accounts.employee_withdrawals.messages.updated'));
    }

    public function employeeWithdrawalsDestroy(Request $request, int $id)
    {
        $withdrawal = EmployeeWithdrawal::query()->findOrFail($id);
        $withdrawal->delete();

        return back()->with('success', __('accounts.employee_withdrawals.messages.deleted'));
    }

    public function employeePenalties()
    {
        return redirect()->route('accounts.penalties');
    }

    public function penaltiesIndex()
    {
        $employees = Employee::query()
            ->where('is_active', true)
            ->orderBy('full_name')
            ->get(['id', 'full_name']);

        $penalties = EmployeePenalty::query()
            ->with(['employee', 'creator'])
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->get();

        return view('accounts.penalties', [
            'title' => __('accounts.penalties.title'),
            'employees' => $employees,
            'penalties' => $penalties,
        ]);
    }

    public function penaltiesStore(Request $request)
    {
        $validated = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'amount' => 'required|integer|min:1',
            'reason' => 'required|string',
        ]);

        EmployeePenalty::query()->create([
            'employee_id' => (int) $validated['employee_id'],
            'amount' => (int) $validated['amount'],
            'reason' => $validated['reason'],
            'created_by' => (int) Auth::id(),
        ]);

        return back()->with('success', __('accounts.penalties.messages.created'));
    }

    public function penaltiesUpdate(Request $request, int $id)
    {
        $penalty = EmployeePenalty::query()->findOrFail($id);

        $validated = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'amount' => 'required|integer|min:1',
            'reason' => 'required|string',
        ]);

        $penalty->forceFill([
            'employee_id' => (int) $validated['employee_id'],
            'amount' => (int) $validated['amount'],
            'reason' => $validated['reason'],
        ])->save();

        return back()->with('success', __('accounts.penalties.messages.updated'));
    }

    public function penaltiesDestroy(Request $request, int $id)
    {
        $penalty = EmployeePenalty::query()->findOrFail($id);
        $penalty->delete();

        return back()->with('success', __('accounts.penalties.messages.deleted'));
    }

    public function employeeBonuses()
    {
        $employees = Employee::query()
            ->where('is_active', true)
            ->orderBy('full_name')
            ->get(['id', 'full_name']);

        $bonuses = EmployeeBonus::query()
            ->with(['employee', 'creator'])
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->get();

        return view('accounts.employee_bonuses', [
            'title' => __('accounts.employee_bonuses.title'),
            'employees' => $employees,
            'bonuses' => $bonuses,
        ]);
    }

    public function employeeBonusesStore(Request $request)
    {
        $validated = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'amount' => 'required|integer|min:1',
            'reason' => 'required|string',
        ]);

        EmployeeBonus::query()->create([
            'employee_id' => (int) $validated['employee_id'],
            'amount' => (int) $validated['amount'],
            'reason' => $validated['reason'],
            'created_by' => (int) Auth::id(),
        ]);

        return back()->with('success', __('accounts.employee_bonuses.messages.created'));
    }

    public function employeeBonusesUpdate(Request $request, int $id)
    {
        $bonus = EmployeeBonus::query()->findOrFail($id);

        $validated = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'amount' => 'required|integer|min:1',
            'reason' => 'required|string',
        ]);

        $bonus->forceFill([
            'employee_id' => (int) $validated['employee_id'],
            'amount' => (int) $validated['amount'],
            'reason' => $validated['reason'],
        ])->save();

        return back()->with('success', __('accounts.employee_bonuses.messages.updated'));
    }

    public function employeeBonusesDestroy(Request $request, int $id)
    {
        $bonus = EmployeeBonus::query()->findOrFail($id);
        $bonus->delete();

        return back()->with('success', __('accounts.employee_bonuses.messages.deleted'));
    }

    public function boxes()
    {
        $cards = [
            [
                'route' => route('accounts.cash-box'),
                'icon' => 'safe',
                'title' => __('accounts.cards.cash_box'),
            ],
            [
                'route' => route('accounts.online-box'),
                'icon' => 'globe2',
                'title' => __('accounts.cards.online_box'),
            ],
            [
                'route' => route('accounts.partners'),
                'icon' => 'person-badge',
                'title' => __('accounts.cards.partners'),
            ],
            [
                'route' => route('accounts.reserve-box'),
                'icon' => 'shield-check',
                'title' => __('accounts.cards.reserve_box'),
            ],
        ];

        return view('accounts.boxes', [
            'title' => __('accounts.boxes.title'),
            'cards' => $cards,
        ]);
    }

    public function reserveBox()
    {
        $reserve = ReserveBox::query()->first();
        $cashReserve = (int) ($reserve?->cash_total ?? 0);
        $onlineReserve = (int) ($reserve?->online_total ?? 0);
        $totalReserve = $cashReserve + $onlineReserve;

        $history = MonthlySettlement::query()
            ->with('creator')
            ->orderByDesc('settled_at')
            ->orderByDesc('id')
            ->get();

        return view('accounts.reserve_box', [
            'title' => __('accounts.reserve_box.title'),
            'cash_reserve' => $cashReserve,
            'online_reserve' => $onlineReserve,
            'total_reserve' => $totalReserve,
            'history' => $history,
            'transactions' => ReserveBoxTransaction::query()
                ->with('creator')
                ->orderByDesc('transaction_date')
                ->orderByDesc('id')
                ->paginate(15),
        ]);
    }

    public function reserveBoxTransferToOwner(Request $request)
    {
        $validated = $request->validate([
            'use_cash' => 'nullable|boolean',
            'use_online' => 'nullable|boolean',
            'cash_amount' => 'nullable|integer|min:1',
            'online_amount' => 'nullable|integer|min:1',
            'notes' => 'nullable|string|max:255',
        ]);

        $useCash = (bool) ($validated['use_cash'] ?? false);
        $useOnline = (bool) ($validated['use_online'] ?? false);

        if (! $useCash && ! $useOnline) {
            return back()->withErrors([
                'use_cash' => __('accounts.reserve_box.errors.select_at_least_one_box'),
            ])->withInput();
        }

        $cashAmount = $useCash ? (int) ($validated['cash_amount'] ?? 0) : 0;
        $onlineAmount = $useOnline ? (int) ($validated['online_amount'] ?? 0) : 0;

        if ($useCash && $cashAmount < 1) {
            return back()->withErrors([
                'cash_amount' => __('accounts.reserve_box.errors.amount_required'),
            ])->withInput();
        }

        if ($useOnline && $onlineAmount < 1) {
            return back()->withErrors([
                'online_amount' => __('accounts.reserve_box.errors.amount_required'),
            ])->withInput();
        }

        try {
            DB::transaction(function () use ($cashAmount, $onlineAmount, $validated) {
                $reserve = ReserveBox::query()->lockForUpdate()->first();
                if (! $reserve) {
                    $reserve = ReserveBox::query()->create([
                        'cash_total' => 0,
                        'online_total' => 0,
                    ]);
                }

                if ($cashAmount > 0 && $cashAmount > (int) $reserve->cash_total) {
                    throw new \RuntimeException(__('accounts.reserve_box.errors.exceeds_balance'));
                }

                if ($onlineAmount > 0 && $onlineAmount > (int) $reserve->online_total) {
                    throw new \RuntimeException(__('accounts.reserve_box.errors.exceeds_balance'));
                }

                $reserve->forceFill([
                    'cash_total' => (int) $reserve->cash_total - $cashAmount,
                    'online_total' => (int) $reserve->online_total - $onlineAmount,
                ])->save();

                $today = Carbon::now()->toDateString();
                $creatorId = (int) Auth::id();

                if ($cashAmount > 0) {
                    AccountTransfer::query()->create([
                        'from_box' => 'reserve_cash',
                        'to_box' => 'partners',
                        'amount' => $cashAmount,
                        'transfer_date' => $today,
                        'created_by' => $creatorId,
                    ]);
                }

                if ($onlineAmount > 0) {
                    AccountTransfer::query()->create([
                        'from_box' => 'reserve_online',
                        'to_box' => 'partners',
                        'amount' => $onlineAmount,
                        'transfer_date' => $today,
                        'created_by' => $creatorId,
                    ]);
                }

                ReserveBoxTransaction::query()->create([
                    'transaction_type' => 'to_owner',
                    'from_box' => $cashAmount > 0 && $onlineAmount > 0 ? 'reserve_cash,reserve_online' : ($cashAmount > 0 ? 'reserve_cash' : 'reserve_online'),
                    'to_box' => 'partners',
                    'cash_amount' => $cashAmount > 0 ? $cashAmount : null,
                    'online_amount' => $onlineAmount > 0 ? $onlineAmount : null,
                    'amount' => $cashAmount + $onlineAmount,
                    'created_by' => $creatorId,
                    'transaction_date' => $today,
                    'notes' => $validated['notes'] ?? null,
                ]);
            });
        } catch (\RuntimeException $e) {
            return back()->withErrors([
                'amount' => $e->getMessage(),
            ])->withInput();
        }

        return back()->with('success', __('accounts.reserve_box.messages.transfer_to_owner_done'));
    }

    public function reserveBoxTransferBetweenBoxes(Request $request)
    {
        $validated = $request->validate([
            'from_box' => 'required|in:reserve_cash,reserve_online',
            'to_box' => 'required|in:reserve_cash,reserve_online',
            'amount' => 'required|integer|min:1',
            'notes' => 'nullable|string|max:255',
        ]);

        if ($validated['from_box'] === $validated['to_box']) {
            return back()->withErrors([
                'to_box' => __('accounts.reserve_box.errors.from_to_same'),
            ])->withInput();
        }

        $amount = (int) $validated['amount'];

        try {
            DB::transaction(function () use ($validated, $amount) {
                $reserve = ReserveBox::query()->lockForUpdate()->first();
                if (! $reserve) {
                    $reserve = ReserveBox::query()->create([
                        'cash_total' => 0,
                        'online_total' => 0,
                    ]);
                }

                $fromKey = $validated['from_box'] === 'reserve_cash' ? 'cash_total' : 'online_total';
                $toKey = $validated['to_box'] === 'reserve_cash' ? 'cash_total' : 'online_total';

                if ($amount > (int) $reserve->{$fromKey}) {
                    throw new \RuntimeException(__('accounts.reserve_box.errors.exceeds_balance'));
                }

                $reserve->forceFill([
                    $fromKey => (int) $reserve->{$fromKey} - $amount,
                    $toKey => (int) $reserve->{$toKey} + $amount,
                ])->save();

                $today = Carbon::now()->toDateString();
                $creatorId = (int) Auth::id();

                ReserveBoxTransaction::query()->create([
                    'transaction_type' => 'transfer_between_reserve_boxes',
                    'from_box' => $validated['from_box'],
                    'to_box' => $validated['to_box'],
                    'amount' => $amount,
                    'created_by' => $creatorId,
                    'transaction_date' => $today,
                    'notes' => $validated['notes'] ?? null,
                ]);
            });
        } catch (\RuntimeException $e) {
            return back()->withErrors([
                'amount' => $e->getMessage(),
            ])->withInput();
        }

        return back()->with('success', __('accounts.reserve_box.messages.transfer_between_done'));
    }

    public function reserveBoxCompensation(Request $request)
    {
        $validated = $request->validate([
            'from_box' => 'required|in:reserve_cash,reserve_online',
            'amount' => 'required|integer|min:1',
            'notes' => 'nullable|string|max:255',
        ]);

        $amount = (int) $validated['amount'];

        try {
            DB::transaction(function () use ($validated, $amount) {
                $reserve = ReserveBox::query()->lockForUpdate()->first();
                if (! $reserve) {
                    $reserve = ReserveBox::query()->create([
                        'cash_total' => 0,
                        'online_total' => 0,
                    ]);
                }

                $fromKey = $validated['from_box'] === 'reserve_cash' ? 'cash_total' : 'online_total';
                $toBox = $validated['from_box'] === 'reserve_cash' ? 'cash' : 'online';
                if ($amount > (int) $reserve->{$fromKey}) {
                    throw new \RuntimeException(__('accounts.reserve_box.errors.exceeds_balance'));
                }

                $reserve->forceFill([
                    $fromKey => (int) $reserve->{$fromKey} - $amount,
                ])->save();

                $today = Carbon::now()->toDateString();
                $creatorId = (int) Auth::id();

                AccountTransfer::query()->create([
                    'from_box' => $validated['from_box'],
                    'to_box' => $toBox,
                    'amount' => $amount,
                    'transfer_date' => $today,
                    'created_by' => $creatorId,
                ]);

                ReserveBoxTransaction::query()->create([
                    'transaction_type' => 'compensation_to_cash_box',
                    'from_box' => $validated['from_box'],
                    'to_box' => $toBox,
                    'amount' => $amount,
                    'created_by' => $creatorId,
                    'transaction_date' => $today,
                    'notes' => $validated['notes'] ?? null,
                ]);
            });
        } catch (\RuntimeException $e) {
            return back()->withErrors([
                'amount' => $e->getMessage(),
            ])->withInput();
        }

        return back()->with('success', __('accounts.reserve_box.messages.compensation_done'));
    }

    public function partners()
    {
        $transfersToOwner = ReserveBoxTransaction::query()
            ->with('creator')
            ->where('transaction_type', 'to_owner')
            ->orderByDesc('transaction_date')
            ->orderByDesc('id')
            ->paginate(15);

        return view('accounts.partners', [
            'title' => __('accounts.cards.partners'),
            'transfersToOwner' => $transfersToOwner,
        ]);
    }

    public function partnersTransferReceipt(ReserveBoxTransaction $transaction)
    {
        if ($transaction->transaction_type !== 'to_owner') {
            abort(404);
        }

        $transaction->load('creator');

        return view('accounts.partners_transfer_receipt', [
            'transaction' => $transaction,
            'printedBy' => Auth::user(),
        ]);
    }

    public function reserveBoxCompensationReceipt(ReserveBoxTransaction $transaction)
    {
        if ($transaction->transaction_type !== 'compensation_to_cash_box') {
            abort(404);
        }

        $transaction->load('creator');

        return view('accounts.reserve_box_compensation_receipt', [
            'transaction' => $transaction,
            'printedBy' => Auth::user(),
        ]);
    }

    public function reserveBoxTransferToOwnerReceipt(ReserveBoxTransaction $transaction)
    {
        if ($transaction->transaction_type !== 'to_owner') {
            abort(404);
        }

        $transaction->load('creator');

        return view('accounts.partners_transfer_receipt', [
            'transaction' => $transaction,
            'printedBy' => Auth::user(),
        ]);
    }

    public function reports()
    {
        return view('accounts.reports');
    }

    public function attendance()
    {
        $cards = [
            [
                'route' => route('accounts.attendance.today'),
                'icon' => 'calendar-check',
                'title' => __('accounts.attendance.cards.today'),
            ],
            [
                'route' => route('accounts.attendance.absences'),
                'icon' => 'person-x',
                'title' => __('accounts.attendance.cards.absences'),
            ],
            [
                'route' => route('accounts.attendance.time-records'),
                'icon' => 'clock-history',
                'title' => __('accounts.attendance.cards.time_records'),
            ],
            [
                'route' => route('accounts.attendance.leaves'),
                'icon' => 'calendar2-week',
                'title' => __('accounts.attendance.cards.leaves'),
            ],
            [
                'route' => route('accounts.attendance.reports'),
                'icon' => 'bar-chart',
                'title' => __('accounts.attendance.cards.reports'),
            ],
        ];

        return view('accounts.attendance', compact('cards'));
    }

    public function attendanceToday()
    {
        $tz = 'Asia/Baghdad';
        $rangeStart = Carbon::now($tz)->startOfDay()->setTimezone('UTC');
        $rangeEnd = Carbon::now($tz)->endOfDay()->setTimezone('UTC');

        $logs = AttendanceLog::query()
            ->with(['user.employee'])
            ->whereBetween('logged_at', [$rangeStart, $rangeEnd])
            ->orderByDesc('logged_at')
            ->get();

        return view('accounts.attendance_today', [
            'title' => __('accounts.attendance.cards.today'),
            'logs' => $logs,
        ]);
    }

    public function attendanceAbsences()
    {
        $tz = 'Asia/Baghdad';

        $from = request()->query('from_date');
        $to = request()->query('to_date');

        $fromLocal = $from ? Carbon::parse($from, $tz)->startOfDay() : Carbon::now($tz)->startOfMonth()->startOfDay();
        $toLocal = $to ? Carbon::parse($to, $tz)->endOfDay() : Carbon::now($tz)->endOfMonth()->endOfDay();

        $rangeStartUtc = $fromLocal->copy()->setTimezone('UTC');
        $rangeEndUtc = $toLocal->copy()->setTimezone('UTC');

        $rows = AttendanceLog::query()
            ->selectRaw('employee_id')
            ->selectRaw('COALESCE(SUM(late_minutes), 0) as late_minutes_total')
            ->selectRaw("COALESCE(SUM(CASE WHEN attendance_status = 'absent' THEN 1 ELSE 0 END), 0) as absent_days_total")
            ->where('type', 'check_in')
            ->whereBetween('logged_at', [$rangeStartUtc, $rangeEndUtc])
            ->groupBy('employee_id')
            ->with(['employee'])
            ->orderByDesc('absent_days_total')
            ->orderByDesc('late_minutes_total')
            ->get();

        $items = $rows->map(function ($row) {
            return [
                'employee_id' => $row->employee_id,
                'employee_name' => $row->employee?->full_name ?? '—',
                'avatar_url' => $row->employee?->avatar_url,
                'late_minutes_total' => (int) ($row->late_minutes_total ?? 0),
                'absent_days_total' => (int) ($row->absent_days_total ?? 0),
            ];
        });

        return view('accounts.attendance_absences', [
            'title' => __('accounts.attendance.cards.absences'),
            'items' => $items,
            'from_date' => $fromLocal->toDateString(),
            'to_date' => $toLocal->toDateString(),
        ]);
    }

    public function attendanceTimeRecords()
    {
        $leaves = Leave::query()
            ->with(['user.employee', 'employee'])
            ->where('leave_type', 'timed')
            ->where('status', 'pending')
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->paginate(15);

        return view('accounts.attendance_time_records', [
            'title' => __('accounts.attendance.cards.time_records'),
            'leaves' => $leaves,
        ]);
    }

    public function attendanceLeaves()
    {
        $status = request()->query('status');
        if (! in_array($status, ['pending', 'approved', 'rejected', 'all'], true)) {
            $status = 'pending';
        }

        $type = request()->query('type');
        if (! in_array($type, ['full_day', 'timed'], true)) {
            $type = null;
        }

        $query = Leave::query()->with(['user.employee', 'employee']);

        if ($status !== 'all') {
            $query->where('status', $status);
        }
        if ($type !== null) {
            $query->where('leave_type', $type);
        }

        $leaves = $query
            ->orderByDesc('leave_date')
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->paginate(15)
            ->appends(request()->query());

        return view('accounts.attendance_leaves', [
            'title' => __('accounts.attendance.cards.leaves'),
            'leaves' => $leaves,
            'status' => $status,
            'type' => $type,
        ]);
    }

    public function attendanceReports()
    {
        $tz = 'Asia/Baghdad';

        $month = request()->query('month');
        $monthLocal = null;
        if (is_string($month) && preg_match('/^\d{4}-\d{2}$/', $month) === 1) {
            $monthLocal = Carbon::createFromFormat('Y-m', $month, $tz)->startOfMonth();
        }
        $monthLocal ??= Carbon::now($tz)->startOfMonth();

        $rangeStartUtc = $monthLocal->copy()->startOfMonth()->setTimezone('UTC');
        $rangeEndUtc = $monthLocal->copy()->endOfMonth()->setTimezone('UTC');
        $rangeStartDate = $monthLocal->copy()->startOfMonth()->toDateString();
        $rangeEndDate = $monthLocal->copy()->endOfMonth()->toDateString();

        $settings = AttendanceSetting::query()->first();
        $lateMinutePrice = max(0, (int) ($settings?->late_minute_price ?? 0));

        $attendanceRows = AttendanceLog::query()
            ->selectRaw('employee_id')
            ->selectRaw("COALESCE(SUM(CASE WHEN attendance_status = 'late' THEN late_minutes ELSE 0 END), 0) as late_minutes_total")
            ->selectRaw("COALESCE(SUM(CASE WHEN attendance_status = 'absent' THEN 1 ELSE 0 END), 0) as absent_days_total")
            ->where('type', 'check_in')
            ->whereBetween('logged_at', [$rangeStartUtc, $rangeEndUtc])
            ->groupBy('employee_id')
            ->get()
            ->keyBy('employee_id');

        $checkInDatesByEmployee = AttendanceLog::query()
            ->where('type', 'check_in')
            ->whereBetween('logged_at', [$rangeStartUtc, $rangeEndUtc])
            ->get(['employee_id', 'logged_at'])
            ->groupBy('employee_id')
            ->map(function ($items) use ($tz) {
                return $items
                    ->map(fn ($log) => $log->logged_at?->timezone($tz)?->toDateString())
                    ->filter()
                    ->unique()
                    ->values()
                    ->all();
            });

        $fullDayLeaveDatesByEmployee = Leave::query()
            ->where('leave_type', 'full_day')
            ->where('status', 'approved')
            ->whereBetween('leave_date', [$rangeStartDate, $rangeEndDate])
            ->get(['employee_id', 'leave_date'])
            ->groupBy('employee_id')
            ->map(function ($items) {
                return $items
                    ->map(fn ($leave) => $leave->leave_date?->toDateString())
                    ->filter()
                    ->unique()
                    ->values()
                    ->all();
            });

        $workDates = [];
        $cursor = $monthLocal->copy()->startOfMonth()->startOfDay();
        $workEnd = $monthLocal->copy()->endOfMonth()->startOfDay();
        while ($cursor->lessThanOrEqualTo($workEnd)) {
            if (! $cursor->isFriday()) {
                $workDates[] = $cursor->toDateString();
            }
            $cursor->addDay();
        }

        $leaveRows = Leave::query()
            ->selectRaw('employee_id')
            ->selectRaw("COALESCE(SUM(CASE WHEN leave_type = 'timed' THEN 1 ELSE 0 END), 0) as timed_leaves_total")
            ->selectRaw("COALESCE(SUM(CASE WHEN leave_type = 'full_day' THEN 1 ELSE 0 END), 0) as full_day_leaves_total")
            ->where('status', 'approved')
            ->whereBetween('leave_date', [$rangeStartDate, $rangeEndDate])
            ->groupBy('employee_id')
            ->get()
            ->keyBy('employee_id');

        $employees = Employee::query()
            ->with('role')
            ->orderBy('full_name')
            ->get();

        $items = $employees->map(function (Employee $employee) use ($tz, $workDates, $attendanceRows, $checkInDatesByEmployee, $fullDayLeaveDatesByEmployee, $leaveRows, $lateMinutePrice) {
            $attendance = $attendanceRows->get($employee->id);
            $leaves = $leaveRows->get($employee->id);

            $lateMinutesTotal = (int) ($attendance?->late_minutes_total ?? 0);
            $absentStatusDaysTotal = (int) ($attendance?->absent_days_total ?? 0);
            $timedLeavesTotal = (int) ($leaves?->timed_leaves_total ?? 0);
            $fullDayLeavesTotal = (int) ($leaves?->full_day_leaves_total ?? 0);

            $checkInDates = $checkInDatesByEmployee->get($employee->id, []);
            $leaveDates = $fullDayLeaveDatesByEmployee->get($employee->id, []);
            $presentOrExcusedDates = array_flip(array_merge($checkInDates, $leaveDates));

            $noShowAbsentDays = 0;
            foreach ($workDates as $d) {
                if (! isset($presentOrExcusedDates[$d])) {
                    $noShowAbsentDays++;
                }
            }

            $absentDaysTotal = $absentStatusDaysTotal + $noShowAbsentDays;

            $lateDeductionTotal = 0;
            if ($lateMinutesTotal > 0) {
                if ($lateMinutePrice > 0) {
                    $lateDeductionTotal = $lateMinutesTotal * $lateMinutePrice;
                } else {
                    $lateDeductionTotal = (int) round($lateMinutesTotal * $employee->minuteRate(\App\Models\Employee::DEFAULT_SALARY_WORKING_DAYS_DIVISOR, $tz));
                }
            }

            return [
                'employee_id' => $employee->id,
                'employee_name' => $employee->full_name ?? '—',
                'employee_phone' => $employee->phone,
                'employee_job_title' => $employee->job_title,
                'employee_role_name' => $employee->role?->name,
                'avatar_url' => $employee->avatar_url,
                'late_minutes_total' => $lateMinutesTotal,
                'late_deduction_total' => $lateDeductionTotal,
                'absent_days_total' => $absentDaysTotal,
                'timed_leaves_total' => $timedLeavesTotal,
                'full_day_leaves_total' => $fullDayLeavesTotal,
            ];
        });

        return view('accounts.attendance_reports', [
            'title' => __('accounts.attendance.cards.reports'),
            'items' => $items,
            'month' => $monthLocal->format('Y-m'),
            'late_minute_price' => $lateMinutePrice,
        ]);
    }

    public function approveTimedLeave(Request $request, Leave $leave)
    {
        if ($leave->leave_type !== 'timed') {
            abort(404);
        }

        if ($leave->status !== 'pending') {
            return back()->with('success', 'تم تحديث الطلب مسبقاً');
        }

        $leave->forceFill(['status' => 'approved'])->save();
        $this->notifyLeaveStatusToEmployee($leave, 'approved');

        return back()->with('success', 'تم قبول الطلب');
    }

    public function rejectTimedLeave(Request $request, Leave $leave)
    {
        if ($leave->leave_type !== 'timed') {
            abort(404);
        }

        if ($leave->status !== 'pending') {
            return back()->with('success', 'تم تحديث الطلب مسبقاً');
        }

        $leave->forceFill(['status' => 'rejected'])->save();
        $this->notifyLeaveStatusToEmployee($leave, 'rejected');

        return back()->with('success', 'تم رفض الطلب');
    }

    public function approveLeave(Request $request, Leave $leave)
    {
        if ($leave->status !== 'pending') {
            return back()->with('success', 'تم تحديث الطلب مسبقاً');
        }

        $leave->forceFill(['status' => 'approved'])->save();
        $this->notifyLeaveStatusToEmployee($leave, 'approved');

        return back()->with('success', 'تم قبول الطلب');
    }

    public function rejectLeave(Request $request, Leave $leave)
    {
        if ($leave->status !== 'pending') {
            return back()->with('success', 'تم تحديث الطلب مسبقاً');
        }

        $leave->forceFill(['status' => 'rejected'])->save();
        $this->notifyLeaveStatusToEmployee($leave, 'rejected');

        return back()->with('success', 'تم رفض الطلب');
    }

    private function notifyLeaveStatusToEmployee(Leave $leave, string $status): void
    {
        if (! \Illuminate\Support\Facades\Schema::hasTable('notifications')) {
            return;
        }

        $leave->loadMissing('user', 'employee');
        if (! $leave->user) {
            return;
        }

        $typeLabel = $leave->leave_type === 'timed' ? 'إجازة زمنية' : 'إجازة يوم كامل';
        $date = $leave->leave_date?->toDateString() ?? '';
        $timePart = '';
        if ($leave->leave_type === 'timed') {
            $from = is_string($leave->leave_time_from) ? trim($leave->leave_time_from) : '';
            $to = is_string($leave->leave_time_to) ? trim($leave->leave_time_to) : '';
            if ($from !== '' && $to !== '') {
                $timePart = ' - '.substr($from, 0, 5).' إلى '.substr($to, 0, 5);
            }
        }

        $approved = $status === 'approved';
        $title = $approved ? 'تمت الموافقة على الإجازة' : 'تم رفض الإجازة';
        $body = $typeLabel.' - '.$date.$timePart;

        $leave->user->notify(new \App\Notifications\SystemAlertNotification($title, $body, [
            'event' => $approved ? 'leave_approved' : 'leave_rejected',
            'leave_id' => $leave->id,
        ]));
    }

    public function cashBox()
    {
        $summary = $this->boxSummary('cash');

        return view('accounts.cash_box', $summary);
    }

    public function onlineBox()
    {
        $summary = $this->boxSummary('online');

        return view('accounts.online_box', $summary);
    }

    private function boxSummary(string $boxKey): array
    {
        $isCash = $boxKey === 'cash';

        $subscriptionPaymentsQuery = SubscriptionPayment::query()->whereNotNull('payment_method');
        if ($isCash) {
            $subscriptionPaymentsQuery->where('payment_method', 'Cash');
        } else {
            $subscriptionPaymentsQuery->where('payment_method', '!=', 'Cash');
        }
        $subscriptionPaymentsTotal = (int) $subscriptionPaymentsQuery->sum('amount');

        $subscriptionsWithoutPaymentsQuery = ServiceSubscription::query()
            ->whereNotNull('payment_method')
            ->whereDoesntHave('payments');
        if ($isCash) {
            $subscriptionsWithoutPaymentsQuery->where('payment_method', 'Cash');
        } else {
            $subscriptionsWithoutPaymentsQuery->where('payment_method', '!=', 'Cash');
        }
        $subscriptionsWithoutPaymentsTotal = (int) $subscriptionsWithoutPaymentsQuery
            ->selectRaw('COALESCE(SUM(GREATEST(COALESCE(price, 0) - COALESCE(discount, 0), 0)), 0) as total')
            ->value('total');

        $activationsQuery = StageAcademyActivation::query()->whereNotNull('payment_method');
        if ($isCash) {
            $activationsQuery->where('payment_method', 'Cash');
        } else {
            $activationsQuery->where('payment_method', '!=', 'Cash');
        }
        $activationsTotal = (int) $activationsQuery->sum('price');

        $expensesQuery = Expense::query();
        if ($isCash) {
            $expensesQuery->where(function ($q) {
                $q->where('payment_method', 'Cash')
                    ->orWhere(function ($nested) {
                        $nested->whereNull('payment_method')
                            ->where('expense_type', '!=', 'online_purchases');
                    });
            });
        } else {
            $expensesQuery->where(function ($q) {
                $q->where(function ($nested) {
                    $nested->whereNotNull('payment_method')
                        ->where('payment_method', '!=', 'Cash');
                })->orWhere(function ($nested) {
                    $nested->whereNull('payment_method')
                        ->where('expense_type', 'online_purchases');
                });
            });
        }
        $expensesTotal = (int) $expensesQuery->sum('total');

        $archivedExpensesQuery = ExpenseArchiveItem::query();
        if ($isCash) {
            $archivedExpensesQuery->where(function ($q) {
                $q->where('payment_method', 'Cash')
                    ->orWhere(function ($nested) {
                        $nested->whereNull('payment_method')
                            ->where('expense_type', '!=', 'online_purchases');
                    });
            });
        } else {
            $archivedExpensesQuery->where(function ($q) {
                $q->where(function ($nested) {
                    $nested->whereNotNull('payment_method')
                        ->where('payment_method', '!=', 'Cash');
                })->orWhere(function ($nested) {
                    $nested->whereNull('payment_method')
                        ->where('expense_type', 'online_purchases');
                });
            });
        }

        $expensesTotal += (int) $archivedExpensesQuery->sum('total');

        $transfersIn = (int) AccountTransfer::query()->where('to_box', $boxKey)->sum('amount');
        $transfersOut = (int) AccountTransfer::query()->where('from_box', $boxKey)->sum('amount');

        $incomingTotal = $subscriptionPaymentsTotal + $subscriptionsWithoutPaymentsTotal + $activationsTotal;
        $balance = $incomingTotal + $transfersIn - $transfersOut - $expensesTotal;

        return [
            'balance' => (int) $balance,
            'incoming_total' => (int) $incomingTotal,
            'subscription_payments_total' => $subscriptionPaymentsTotal,
            'subscriptions_without_payments_total' => $subscriptionsWithoutPaymentsTotal,
            'stage_academy_total' => $activationsTotal,
            'expenses_total' => $expensesTotal,
            'transfers_in' => $transfersIn,
            'transfers_out' => $transfersOut,
        ];
    }

    public function debts(Request $request)
    {
        $query = ServiceSubscription::query()
            ->with(['customer', 'service'])
            ->whereRaw(
                '(COALESCE(service_subscriptions.price, 0) - COALESCE(service_subscriptions.discount, 0)) > (SELECT COALESCE(SUM(subscription_payments.amount), 0) FROM subscription_payments WHERE subscription_payments.service_subscription_id = service_subscriptions.id)'
            );

        $parts = $this->smartSearchParts($request->query('search'));
        if ($parts['search'] !== '') {
            $search = $parts['search'];
            $searchRaw = $parts['searchRaw'];
            $searchDigits = $parts['searchDigits'];
            $terms = $parts['terms'];

            $query->where(function ($q) use ($search, $searchRaw, $searchDigits, $terms) {
                $q->where('invoice_number', 'like', '%'.$search.'%')
                    ->orWhereHas('customer', function ($cq) use ($searchRaw, $searchDigits, $terms) {
                        $cq->where(function ($nameQ) use ($terms) {
                            foreach ($terms as $t) {
                                $nameQ->where('name', 'like', '%'.$t.'%');
                            }
                        })->orWhere('phone', 'like', '%'.$searchRaw.'%')
                            ->orWhere('whatsapp', 'like', '%'.$searchRaw.'%');

                        if ($searchDigits !== '') {
                            $cq->orWhere('phone', 'like', '%'.$searchDigits.'%')
                                ->orWhere('whatsapp', 'like', '%'.$searchDigits.'%');
                        }
                    });
            });
        }

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

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

        $invoices = $query
            ->withSum('payments as total_paid', 'amount')
            ->orderByDesc('created_at')
            ->paginate(15)
            ->appends($request->all());

        return view('accounts.debts', compact('invoices'));
    }

    public function debtPayments($invoiceId)
    {
        $invoice = ServiceSubscription::query()
            ->with(['customer', 'service', 'payments.creator'])
            ->findOrFail($invoiceId);

        $total = (int) max(0, ((int) $invoice->price - (int) $invoice->discount));
        $paid = (int) $invoice->payments->sum('amount');
        $remaining = (int) max(0, $total - $paid);

        return view('accounts.debt_payments', compact('invoice', 'total', 'paid', 'remaining'));
    }

    public function debtInvoice($invoiceId)
    {
        app()->setLocale('en');

        $subscription = ServiceSubscription::query()
            ->with(['customer', 'creator', 'payments.creator'])
            ->findOrFail($invoiceId);

        return view('accounts.debt_invoice', compact('subscription'));
    }

    public function storeDebtPayment(Request $request, $invoiceId)
    {
        $invoice = ServiceSubscription::query()->with('payments')->findOrFail($invoiceId);

        $total = (int) max(0, ((int) $invoice->price - (int) $invoice->discount));
        $paid = (int) $invoice->payments->sum('amount');
        $remaining = (int) max(0, $total - $paid);

        $validated = $request->validate([
            'paid_amount' => 'required|integer|min:1',
            'payment_date' => 'required|date',
            'notes' => 'nullable|string',
        ]);

        if ((int) $validated['paid_amount'] > $remaining) {
            return redirect()->back()->withErrors([
                'paid_amount' => __('accounts.debts.validation.amount_exceeds_remaining'),
            ]);
        }

        SubscriptionPayment::create([
            'service_subscription_id' => $invoice->id,
            'amount' => (int) $validated['paid_amount'],
            'payment_method' => $invoice->payment_method ?: 'Cash',
            'payment_date' => $validated['payment_date'],
            'notes' => $validated['notes'] ?? null,
            'created_by' => Auth::id(),
        ]);

        return redirect()->route('accounts.debts.payments', $invoice->id)
            ->with('success', __('accounts.debts.messages.payment_added'));
    }

    public function updateDebtPayment(Request $request, $paymentId)
    {
        $payment = SubscriptionPayment::query()->with('subscription.payments')->findOrFail($paymentId);
        $invoice = $payment->subscription;

        $total = (int) max(0, ((int) $invoice->price - (int) $invoice->discount));
        $paidWithoutThis = (int) $invoice->payments->where('id', '!=', $payment->id)->sum('amount');
        $remainingPlusThis = (int) max(0, $total - $paidWithoutThis);

        $validated = $request->validate([
            'paid_amount' => 'required|integer|min:1',
            'payment_date' => 'required|date',
            'notes' => 'nullable|string',
        ]);

        if ((int) $validated['paid_amount'] > $remainingPlusThis) {
            return redirect()->back()->withErrors([
                'paid_amount' => __('accounts.debts.validation.amount_exceeds_remaining'),
            ]);
        }

        $payment->update([
            'amount' => (int) $validated['paid_amount'],
            'payment_date' => $validated['payment_date'],
            'notes' => $validated['notes'] ?? null,
        ]);

        return redirect()->route('accounts.debts.payments', $invoice->id)
            ->with('success', __('accounts.debts.messages.payment_updated'));
    }

    public function destroyDebtPayment($paymentId)
    {
        $payment = SubscriptionPayment::query()->with('subscription')->findOrFail($paymentId);
        $invoiceId = $payment->subscription->id;

        $payment->delete();

        return redirect()->route('accounts.debts.payments', $invoiceId)
            ->with('success', __('accounts.debts.messages.payment_deleted'));
    }

    public function transfers(Request $request)
    {
        $query = AccountTransfer::query()->with('creator');

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

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

        $transfers = $query
            ->orderByDesc('transfer_date')
            ->orderByDesc('id')
            ->paginate(15)
            ->appends($request->all());

        $boxes = [
            'cash' => __('accounts.transfers.boxes.cash'),
            'online' => __('accounts.transfers.boxes.online'),
        ];

        $boxBalances = $this->currentBoxBalances();

        return view('accounts.transfers', compact('transfers', 'boxes', 'boxBalances'));
    }

    public function storeTransfer(Request $request)
    {
        $boxes = ['cash', 'online'];

        $validator = Validator::make($request->all(), [
            'amount' => ['required', 'integer', 'min:1'],
            'from_box' => ['required', 'string', 'in:'.implode(',', $boxes)],
            'to_box' => ['required', 'string', 'in:'.implode(',', $boxes), 'different:from_box'],
            'transfer_date' => ['required', 'date'],
        ]);

        if ($validator->fails()) {
            return redirect()
                ->route('accounts.transfers')
                ->withErrors($validator)
                ->withInput()
                ->with('open_transfer_add_modal', true);
        }

        $validated = $validator->validated();
        $boxBalances = $this->currentBoxBalances();
        $fromBalance = (int) ($boxBalances[$validated['from_box']] ?? 0);

        if ((int) $validated['amount'] > $fromBalance) {
            return redirect()
                ->route('accounts.transfers')
                ->withErrors(['amount' => __('accounts.transfers.insufficient_balance', ['available' => number_format($fromBalance, 0)])])
                ->withInput()
                ->with('open_transfer_add_modal', true);
        }

        AccountTransfer::create([
            'amount' => (int) $validated['amount'],
            'from_box' => $validated['from_box'],
            'to_box' => $validated['to_box'],
            'transfer_date' => $validated['transfer_date'],
            'created_by' => Auth::id(),
        ]);

        return redirect()->route('accounts.transfers')
            ->with('success', __('accounts.transfers.messages.created'));
    }

    public function updateTransfer(Request $request, $id)
    {
        $transfer = AccountTransfer::query()->findOrFail($id);
        $boxes = ['cash', 'online'];

        $validator = Validator::make($request->all(), [
            'amount' => ['required', 'integer', 'min:1'],
            'from_box' => ['required', 'string', 'in:'.implode(',', $boxes)],
            'to_box' => ['required', 'string', 'in:'.implode(',', $boxes), 'different:from_box'],
            'transfer_date' => ['required', 'date'],
        ]);

        if ($validator->fails()) {
            return redirect()
                ->route('accounts.transfers')
                ->withErrors($validator)
                ->withInput()
                ->with('edit_transfer_id', $transfer->id);
        }

        $validated = $validator->validated();
        $boxBalances = $this->currentBoxBalancesWithoutTransfer($transfer);
        $fromBalance = (int) ($boxBalances[$validated['from_box']] ?? 0);

        if ((int) $validated['amount'] > $fromBalance) {
            return redirect()
                ->route('accounts.transfers')
                ->withErrors(['amount' => __('accounts.transfers.insufficient_balance', ['available' => number_format($fromBalance, 0)])])
                ->withInput()
                ->with('edit_transfer_id', $transfer->id);
        }

        $transfer->update([
            'amount' => (int) $validated['amount'],
            'from_box' => $validated['from_box'],
            'to_box' => $validated['to_box'],
            'transfer_date' => $validated['transfer_date'],
        ]);

        return redirect()->route('accounts.transfers')
            ->with('success', __('accounts.transfers.messages.updated'));
    }

    public function destroyTransfer($id)
    {
        $transfer = AccountTransfer::query()->findOrFail($id);
        $transfer->delete();

        return redirect()->route('accounts.transfers')
            ->with('success', __('accounts.transfers.messages.deleted'));
    }

    private function currentBoxBalances(): array
    {
        return [
            'cash' => (int) ($this->boxSummary('cash')['balance'] ?? 0),
            'online' => (int) ($this->boxSummary('online')['balance'] ?? 0),
        ];
    }

    private function currentBoxBalancesWithoutTransfer(AccountTransfer $transfer): array
    {
        $balances = $this->currentBoxBalances();

        if (isset($balances[$transfer->from_box])) {
            $balances[$transfer->from_box] = (int) $balances[$transfer->from_box] + (int) $transfer->amount;
        }

        if (isset($balances[$transfer->to_box])) {
            $balances[$transfer->to_box] = (int) $balances[$transfer->to_box] - (int) $transfer->amount;
        }

        return $balances;
    }
}
