<?php

namespace Modules\SpecialAttendance\Http\Repositories;

use Carbon\Carbon;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Modules\Tardy\Entities\TardyRule;
use Modules\Tardy\Entities\TardyRecord;
use App\Helpers\CoreApp\Traits\FileHandler;
use Modules\SpecialAttendance\Entities\Deduction;
use Modules\SpecialAttendance\Entities\DutyCalendar;
use App\Models\coreApp\Relationship\RelationshipTrait;
use Modules\SpecialAttendance\Entities\DeductionDetail;

class DeductionRepository
{
    use FileHandler, RelationshipTrait;

    protected $model;

    public function __construct()
    {
        $this->model = new Deduction;
    }

    public function fields()
    {
        return [
            _trans('common.ID'),
            _trans('common.Employee'),
            _trans('common.Month'),
            _trans('common.Amount'),
            _trans('common.Consider Amount'),
            _trans('common.Action'),
        ];
    }

    public function table($request)
    {
        $data = $this->model->query()->with('user')->where('company_id', auth()->user()->company_id);

        if (!isAdminOrOfficeManager()) {
            $data = $data->where('user_id', auth()->id());
        }

        $data = $data
            ->when(request('search'), fn($q) => $q->whereHas('user', function ($q) {
                $q->where('name', 'like', '%' . request('search') . '%');
            }))
            ->orderBy('id', 'desc')
            ->paginate($request->limit ?? 2);

        return [
            'data' => $data->map(function ($data) {
                $action_button = '';

                if (hasPermission('deduction_generate_read')) {
                    $action_button .= actionButton('View', route('deduction.show', ['id' => $data->id]));
                }

                if (hasPermission('deduction_generate_update') && $data->consider_amount == 0 && isAdmin()) {
                    $action_button .= actionButton('Consider', route('deduction.show', ['id' => $data->id, 'for_consider' => 1]));
                }

                $button = ' <div class="dropdown dropdown-action">
                                    <button type="button" class="btn-dropdown" data-bs-toggle="dropdown"
                                        aria-expanded="false">
                                        <i class="fa-solid fa-ellipsis"></i>
                                    </button>
                                    <ul class="dropdown-menu dropdown-menu-end">
                                    ' . $action_button . '
                                    </ul>
                                </div>';

                return [
                    'id' => $data->id,
                    'employee' => $data->user->name,
                    'month' => Carbon::parse($data->year . '-' . $data->month)->format('M Y'),
                    'amount' => $data->amount,
                    'consider_amount' => $data->consider_amount ?? 0,
                    'action' => $button,
                ];
            }),
            'pagination' => [
                'total' => $data->total(),
                'count' => $data->count(),
                'per_page' => $data->perPage(),
                'current_page' => $data->currentPage(),
                'total_pages' => $data->lastPage(),
                'pagination_html' => $data->links('backend.pagination.custom')->toHtml(),
            ],
        ];
    }

    public function store($user_id, $year, $month, $purpose, $amount, $days, $deduction_type = 'Leave', $tardy_rule_id = null)
    {
        $deduction = Deduction::firstOrCreate([
            'company_id' => auth()->user()->company_id,
            'branch_id' => getCurrentBranch(),
            'user_id' => $user_id,
            'year' => $year,
            'month' => $month,
        ], [
            'amount' => $amount,
            'created_by' => auth()->id(),
        ]);

        $this->storeDeductionDetails($deduction, $deduction_type, $tardy_rule_id, $purpose, $amount, $days);
        $this->updateDeductionAmount($deduction);
    }

    public function storeDeductionDetails($deduction, $deduction_type, $tardy_rule_id, $purpose, $amount, $days)
    {
        DeductionDetail::updateOrCreate([
            'deduction_id' => $deduction->id,
            'deduction_type' => $deduction_type,
            'tardy_rule_id' => $tardy_rule_id ?? null,
        ], [
            'purpose' => $purpose,
            'amount' => $amount,
            'days' => $days,
            'created_by' => auth()->id(),
            'updated_by' => auth()->id(),
        ]);
    }

    public function updateDeductionAmount($deduction)
    {
        $amount = DeductionDetail::where('deduction_id', $deduction->id)->sum('amount');

        $deduction->update([
            'amount' => $amount,
            'updated_by' => auth()->id(),
        ]);
    }

    public function storeDeductionFromLeaveApprove($leaveRequest)
    {
        $totalWeekendDay = DutyCalendar::query()
            ->whereMonth('date', Carbon::parse($leaveRequest->leave_from)->format('m'))
            ->whereYear('date', Carbon::parse($leaveRequest->leave_from)->format('Y'))
            ->where('employee_id', $leaveRequest->user_id)
            ->where('is_weekend', 1)
            ->count();

        $totalDay = cal_days_in_month(CAL_GREGORIAN, Carbon::parse($leaveRequest->leave_from)->format('m'), Carbon::parse($leaveRequest->leave_from)->format('Y'));
        $totalWorkingDay = $totalDay - $totalWeekendDay;
        $perDaySalary = $leaveRequest->user->basic_salary / $totalWorkingDay;

        $unpaidDates = array_filter(request('dates'), function ($date) {
            return ! isset(request('is_count_as_paid')[$date]);
        });

        $unpaidByMonth = [];

        foreach ($unpaidDates as $date) {
            $yearMonth = date('Y-m', strtotime($date));
            if (! isset($unpaidByMonth[$yearMonth])) {
                $unpaidByMonth[$yearMonth] = 0;
            }
            $unpaidByMonth[$yearMonth]++;
        }

        foreach ($unpaidByMonth as $yearMonth => $count) {
            $this->store($leaveRequest->user_id, Carbon::parse($yearMonth)->format('Y'), Carbon::parse($yearMonth)->format('m'), _trans('common.Unpaid Leave'), $perDaySalary * $count, $count);
        }
    }

    public function storeAbsentDeduction($user_id, $year, $month, $perDaySalary)
    {
        $absentDays = DutyCalendar::query()
            ->where('employee_id', $user_id)
            ->whereMonth('date', $month)
            ->whereYear('date', $year)
            ->where('date', '<=', date('Y-m-d'))
            ->whereDoesntHave('attendance')
            ->whereNotNull('duty_schedule_id')
            ->count();

        if ($absentDays > 0) {
            $this->store($user_id, $year, $month, _trans('common.Absent'), $perDaySalary * $absentDays, $absentDays, 'Attendance');
        }
    }

    public function storeDeductionFromTardyRecordsWithAbsent($request)
    {
        $year = Carbon::parse($request->month ? $request->month : date('Y-m'))->format('Y');
        $month = Carbon::parse($request->month ? $request->month : date('Y-m'))->format('m');

        if ($request->generate_by == 'department') {
            $users = User::where('department_id', $request->department_id)->get(['id', 'basic_salary']);
        } else {
            $users = User::where('id', $request->user_id)->get(['id', 'basic_salary']);
        }

        DB::transaction(function () use ($users, $year, $month) {
            foreach ($users ?? [] as $user) {
                $tardyRecords = TardyRecord::where('user_id', $user->id)
                    ->where(function ($q) {
                        $q->whereHas('tardyRequest', function ($query) {
                            $query->where('is_approved', 0);
                        })->orWhereDoesntHave('tardyRequest');
                    })
                    ->with('tardyRule')
                    ->whereYear('date', $year)
                    ->whereMonth('date', $month)
                    ->get(['id', 'tardy_rule_id', 'date', 'tardy_time']);

                $totalWeekendDay = DutyCalendar::query()
                    ->whereMonth('date', $month)
                    ->whereYear('date', $year)
                    ->where('employee_id', $user->id)
                    ->where('is_weekend', 1)
                    ->count();

                $totalDay = cal_days_in_month(CAL_GREGORIAN, $month, $year);
                $totalWorkingDay = $totalDay - $totalWeekendDay;


                $perDaySalary = $user->basic_salary ? $user->basic_salary / $totalWorkingDay : 0;

                $tardyRuleIds = $tardyRecords->pluck('tardy_rule_id')->toArray();

                $rules = TardyRule::whereIn('id', $tardyRuleIds)->where('status_id', 1)->get(['id', 'title', 'threshold_minutes', 'threshold_days', 'deduction_days']);

                foreach ($rules as $rule) {
                    if ($rule->threshold_days > 0) {

                        $filteredRecords = $tardyRecords->where('tardy_rule_id', $rule->id)->filter(function ($record) use ($rule) {
                            return convertTimeToMinutes($record->tardy_time) >= $rule->threshold_minutes;
                        });

                        $tardyCount = $filteredRecords->count();
                        $cycles = intdiv($tardyCount, $rule->threshold_days);
                        $deductionDays = $cycles * $rule->deduction_days;
                    } else {
                        $deductionDays = $rule->deduction_days;
                    }

                    if ($deductionDays > 0) {
                        $this->store($user->id, $year, $month, $rule->title, $perDaySalary * $deductionDays, $deductionDays, 'Attendance', $rule->id);
                    }
                }

                $this->storeAbsentDeduction($user->id, $year, $month, $perDaySalary);

                $deduction = Deduction::where([
                    'user_id' => $user->id,
                    'year'    => $year,
                    'month'   => $month,
                ])->first();

                if ($deduction && $deduction->amount > 0) {
                    $params['message'] = '<b>' . $deduction->user->name . ' [' . $deduction->user->employee_id . "]</b> <b class='text-dark'>" . Carbon::parse($deduction->year . '-' . $deduction->month)->format('F') . ' ' . $deduction->year . "</b> deduction amount: <b class='text-warning'>[" . $deduction->amount . '].</b> Please appeal within two days.';

                    $params['receivers'][] = $user->id;
                    $office_manager_id = User::whereHas('role', fn($q) => $q->where('slug', 'officemanager'))->first()?->id;
                    if ($office_manager_id) {
                        $params['receivers'][] = $office_manager_id;
                    }

                    $params['redirectUrlForWeb'] = route('deduction.show', $deduction->id);
                    $params['sendOnlyAdmin'] = true;
                    $params['forSelf'] = false;

                    // Send notification
                    storeNotification($params);
                }
            }
        });
    }

    public function show($id)
    {
        $data['deduction'] = Deduction::where('id', $id)->with('deductionDetails', 'user')->first();

        $title = request('for_consider') ? _trans('common.Deduction Consider for: ') : _trans('common.Deduction Info: ');
        $data['title'] = $title . Carbon::parse($data['deduction']->year . '-' . $data['deduction']->month)->format('M') . ' ' . $data['deduction']->year;

        return $data;
    }

    public function consider($id)
    {
        DB::transaction(function () use ($id) {
            $totalConsiderAmount = 0;
            $deductionDetails = DeductionDetail::whereNotIn('id', request('not_applicable_ids') ?? [])->get();

            foreach ($deductionDetails ?? [] as $deductionDetail) {
                $totalConsiderAmount += $deductionDetail->amount;

                $deductionDetail->update([
                    'is_applicable' => 0,
                    'considered_by' => auth()->id(),
                    'considered_at' => now(),
                    'updated_by' => auth()->id(),
                ]);
            }

            $deduction = Deduction::where('id', $id)->first();
            $deduction->update([
                'consider_amount' => $totalConsiderAmount,
                'updated_by' => auth()->id(),
                'appeal_status' => $deduction->is_appealed ? 5 : $deduction->appeal_status,
                'appeal_approved_by' => $deduction->is_appealed ? auth()->id() : $deduction->appeal_approved_by,
                'appeal_approved_at' => $deduction->is_appealed ? now() : $deduction->appeal_approved_at,
            ]);

            if ($totalConsiderAmount > 0) {
                $params['message'] = '<b>' . $deduction->user->name . ' [' . $deduction->user->employee_id . "]</b> <b class='text-dark'>" . Carbon::parse($deduction->month)->format('F') . ' ' . $deduction->year . "</b> deduction amount: <b class='text-warning'>" . $deduction->amount . ".</b> has been considered. Consider amount: <b class='text-warning'>" . $deduction->consider_amount . '.</b>';

                $params['receivers'][] = $deduction->user_id;
                $office_manager_id = User::whereHas('role', fn($q) => $q->where('slug', 'officemanager'))->first()?->id;
                if ($office_manager_id) {
                    $params['receivers'][] = $office_manager_id;
                }

                $params['redirectUrlForWeb'] = route('deduction.show', $deduction->id);
                $params['sendOnlyAdmin'] = true;
                $params['forSelf'] = false;
                storeNotification($params);
            }
        });
    }

    public function appeal($id)
    {
        $deduction = Deduction::where('id', $id)->first();

        if (request('attachment') && request()->hasFile('attachment')) {
            $attachment_id = $this->uploadImage(request('attachment'), 'uploads/deduction/attachment')->id;
        }

        $deduction->update([
            'is_appealed' => 1,
            'appealed_at' => now(),
            'appeal_details' => request('appeal_details'),
            'appeal_attachment_id' => request('appeal_attachment_id') ? request('appeal_attachment_id') : @$attachment_id ?? null,
            'appeal_status' => 2,
        ]);

        $params['message'] = '<b>' . $deduction->user->name . ' [' . $deduction->user->employee_id . "]</b> has been appealed for <b class='text-dark'>" . Carbon::parse($deduction->year . '-' . $deduction->month)->format('M Y') . "</b> <b class='text-warning'>deduction</b> <b class='text-success'>consideration</b>";
        $params['redirectUrlForWeb'] = route('deduction.show', ['id' => $deduction->id, 'for_consider' => 1]);
        $params['sendOnlyAdmin'] = true;
        $params['forSelf'] = false;

        storeNotification($params);
    }
}
