<?php
namespace App\Http\Controllers\API\v1;

use App\Http\Controllers\Controller;
use App\Models\AttendancePosting;
use App\Models\AttendancePostingDtls;
use App\Models\Company;
use App\Models\Dealership;
use App\Models\Employee;
use App\Models\Payroll;
use App\Models\PayrollDate;
use App\Models\PayrollSummary;
use App\Models\RoleCompanyAccess;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Carbon\Carbon;
use DateTime;
use Illuminate\Support\Facades\Auth;

class PayrollDateController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(Request $request)
    {
        $user = Auth::user();
        $keyword = $request->input('keyword', null);
        if($keyword) $keyword = str_replace(' ','%', $keyword);
        $perPage = $request->input('per_page', PHP_INT_MAX);
        $sortBy = $request->input('sortBy', null);
        $sortType = $request->input('sortType', null);

        $rolesWithSystemId3 = $user->roles->filter(function($role) {
            return $role->group && $role->group->system_id === 3;
        });

        // Get dealers for ALL roles with system_id 3
        $companies = [];
        if ($rolesWithSystemId3->isNotEmpty()) {
            $roleIds = $rolesWithSystemId3->pluck('id')->toArray();

            $companies = RoleCompanyAccess::whereIn('role_id', $roleIds)
                ->pluck('company_id')
                ->unique()
                ->toArray();
        }

        $data = PayrollDate::with(['company','dealership'])
            ->when(!empty($companies), function($q) use ($companies) {
                // If user has company access, only show those companies
                $q->whereIn('company_id', $companies);
            }, function($q) {
                // If no company access, show only records with null company_id
                $q->whereNull('company_id');
            })
            ->when($keyword, fn($q) => $q->whereHas('company', fn($qc) => $qc->where('name', 'like', '%'.$keyword.'%'))->orWhereHas('dealership', fn($qc) => $qc->where('name', 'like', '%'.$keyword.'%')))
            ->when($sortBy, fn ($q) => $q->orderBy($sortBy, $sortType))
            ->orderBy('payroll_date')
            ->paginate($perPage);

        if($data->isEmpty()){
            $data = PayrollDate::with(['company','dealership'])
            ->whereNull('company_id')
            ->when($keyword, fn($q) => $q->whereHas('company', fn($qc) => $qc->where('name', 'like', '%'.$keyword.'%'))->orWhereHas('dealership', fn($qc) => $qc->where('name', 'like', '%'.$keyword.'%')))
            ->when($sortBy, fn ($q) => $q->orderBy($sortBy, $sortType))
            ->orderBy('payroll_date')
            ->paginate($perPage);

        }

        return response()->json($data);
    }


    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        // Validate the incoming request data
        $validator = Validator::make($request->all(), [
            'payroll_date' => 'required|date_format:Y-m-d',
        ]);

        /** If validation fails return with error message */
        if ($validator->fails()) {
            $errors = $validator->errors();
            $first = $errors->getMessages();
            return response()->json([
                'status'  => false,
                'message' => (reset($first))[0],
                'errors'  => $errors
            ], 422);
        }

        DB::connection()->beginTransaction();

        try {


            PayrollDate::create($request->all());

            DB::connection()->commit();

            return response()->json([
                'message' => 'Record Successfully added!',
                'status' => 'success',
            ],201);

        } catch (\Throwable $e) {
            DB::connection()->rollback();
            return response()->json([
                'status'  => false,
                'message' => 'Unable to process request. Please try again.',
                'data'    => $e->getMessage()
            ], 422);
        }
    }
    /**
     * Store a batch of newly created resource in storage.
     */
    public function storeBatch(Request $request)
    {
        // Validate the incoming request data
        $validator = Validator::make($request->all(), [
            'start_month' => 'required|date_format:Y-m',
            'end_month' => 'required|date_format:Y-m|after_or_equal:start_month',
            'days' => 'required|array',
            'days.*' => 'integer|min:1|max:31', // Days as integers (1-31 to handle months with different day ranges)
        ]);

        /** If validation fails return with error message */
        if ($validator->fails()) {
            $errors = $validator->errors();
            $first = $errors->getMessages();
            return response()->json([
                'status'  => false,
                'message' => (reset($first))[0],
                'errors'  => $errors
            ], 422);
        }

        DB::connection()->beginTransaction();

        try {

            $startMonth = Carbon::parse($request['start_month'] . '-01');
            $endMonth = Carbon::parse($request['end_month'] . '-01');
            $selectedDays = $request['days']; // Day numbers

            $dates = [];

            // Loop through each month in the range
            for ($month = $startMonth; $month->lte($endMonth); $month->addMonth()) {
                $daysInMonth = $month->daysInMonth;

                // Loop through each selected day and validate if it exists in the current month
                foreach ($selectedDays as $day) {
                    if ($day <= $daysInMonth) {
                        $currentDate = $month->copy()->day($day);
                        $dates[] = $currentDate->format('Y-m-d');
                    }
                }
            }

            foreach($dates as $date) {
                $date_from = '';
                $date_to = '';

                $temp_date = $date;
                $date = new DateTime($date);

                if (date('d', strtotime($temp_date)) == 15) {
                    $date_to_temp = clone $date; // Clone to avoid modification issues
                    $prev_month = clone $date; // Clone again before modifying
                    $prev_month->modify('-1 month');

                    // Set the specific day for date_from and date_to
                    $date_from = $prev_month->setDate($prev_month->format('Y'), $prev_month->format('m'), 21);
                    $date_to = $date_to_temp->setDate($date_to_temp->format('Y'), $date_to_temp->format('m'), 5);

                    // echo $temp_date . ' - ' . $date_from->format('Y-m-d') . ' - ' . $date_to->format('Y-m-d') . '<br>';
                } else if(date('d', strtotime($temp_date)) == 30) {
                    $date_to_temp = clone $date; // Clone to avoid modification issues
                    $prev_month = clone $date; // Clone again before modifying

                    // Set the specific day for date_from and date_to
                    $date_from = $prev_month->setDate($prev_month->format('Y'), $prev_month->format('m'), 6);
                    $date_to = $date_to_temp->setDate($date_to_temp->format('Y'), $date_to_temp->format('m'), 20);

                    // echo $temp_date . ' - ' . $date_from->format('Y-m-d') . ' - ' . $date_to->format('Y-m-d') . '<br>';
                }

                // dd(date('d', strtotime($date)));

                $data = array_merge(
                    $request->only(['company_id','dealership_id']),
                    [
                        'payroll_date' => $date,
                        'date_from' => $date_from->format('Y-m-d') ?? null,
                        'date_to' => $date_to->format('Y-m-d') ?? null,
                    ]
                );
                PayrollDate::create($data);
            }

            // dd();
            DB::connection()->commit();

            return response()->json([
                'message' => 'Record Successfully added!',
                'status' => 'success',
            ],201);

        } catch (\Throwable $e) {
            DB::connection()->rollback();
            return response()->json([
                'status'  => false,
                'message' => 'Unable to process request. Please try again.',
                'data'    => $e->getMessage()
            ], 422);
        }
    }

    /**
     * Display the specified resource.
     */
    public function show(PayrollDate $cutoffSetting)
    {
        //
    }



    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, int $id)
    {
        $data = PayrollDate::where('id', $id)->first();

        if (!$data) {
            return response()->json(['messages' => 'No data found.'], 404);
        }

        // Validate the incoming request data
        $validator = Validator::make($request->all(), [
            'payroll_date' => 'required|date_format:Y-m-d',
        ]);

        /** If validation fails return with error message */
        if ($validator->fails()) {
            $errors = $validator->errors();
            $first = $errors->getMessages();
            return response()->json([
                'status'  => false,
                'message' => (reset($first))[0],
                'errors'  => $errors
            ], 422);
        }

        DB::connection()->beginTransaction();

        try {

            $data->fill($request->all());
            $data->save();

            $data->touch();

            DB::connection()->commit();

            return response()->json([
                'message' => 'Record Successfully updated!',
                'status' => 'success',
                'data' => $data,
            ],201);

        } catch (Throwable $e) {
            DB::connection()->rollback();
            return response()->json([
                'status'  => false,
                'message' => 'Unable to process request. Please try again.',
                'data'    => $e->getMessage()
            ]);
        }
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(int $id)
    {
        $data = PayrollDate::find($id);
        if(!$data)
        {
            return response()->json(['message' => "Record not found!"],204);
        }
        DB::connection()->beginTransaction();
        try {

            $data->delete();
            DB::connection()->commit();
            return response()->json(['message' => "Record successfully deleted!"],201);

        } catch (Throwable $e) {
            DB::connection()->rollback();
            return response()->json([
                'status'  => false,
                'message' => 'Unable to process request. Please try again.',
                'data'    => $e->getMessage()
            ]);
        }
    }

    public function getCutOff(Request $request)
    {
        $company_id = $request->company_id;
        $payroll_date = $request->payroll_date;

        $salary_date = Carbon::parse($payroll_date)->format('j');
        // $cut_off = \App\Models\CutoffSetting::where(fn($q) => $q->where('company_id', $company_id)->orWhereNull('company_id'))
        //     ->where('salary_day', $salary_date)
        //     ->first();
        $cut_off = \App\Models\CutoffSetting::where('company_id', $company_id)
            ->where('salary_day', $salary_date)
            ->first();
        if(!$cut_off) {
            $cut_off = \App\Models\CutoffSetting::whereNull('company_id')
                ->where('salary_day', $salary_date)
                ->first();

        }

        $start_month = Carbon::parse($payroll_date)->format('Y-m');

        // Adjust the logic based on the cutoff days
        if ($cut_off->day_start > $cut_off->day_end) {
            // When day_start is greater than day_end, start_month should be the previous month
            $start_month = Carbon::parse($start_month . '-01')->subMonth()->format('Y-m');
            $end_month = Carbon::parse($start_month . '-01')->addMonth()->format('Y-m');
        } else {
            // Otherwise, use the current month for both start and end
            $end_month = $start_month;
        }

        // Construct and format day_start and day_end with Y-m-d
        $day_start_date = Carbon::parse($start_month . '-' . $cut_off->day_start);
        $day_end_date = Carbon::parse($end_month . '-' . $cut_off->day_end);

        // Generate array of dates from start to end
        $date_range = [];
        $current_date = $day_start_date->copy();

        while ($current_date->lte($day_end_date)) {
            $date_range[] = $current_date->format('Y-m-d');
            $current_date->addDay(); // Increment by 1 day
        }

        return response()->json([
            'cut_off' => $cut_off->cut_off,
            'start_date' => $day_start_date->format('Y-m-d'),
            'end_date' => $day_end_date->format('Y-m-d'),
            'date_range' => $date_range
        ]);
    }

    public function getPostedAttendance()
    {
        $postedAttendances = AttendancePosting::with(['dealership'])->get();

        $data = [];

        foreach($postedAttendances as $posetedAttendance){
            // dd($posetedAttendance->payroll_date);
            $reference_no = $posetedAttendance->reference_no;
            $payroll_date = $posetedAttendance->payroll_date;
            $salary_date = Carbon::parse($payroll_date)->format('j');
            $cut_off = \App\Models\CutoffSetting::where(fn($q) => $q->where('company_id', $posetedAttendance->company_id)->orWhereNull('company_id'))
                ->where('salary_day', $salary_date)
                ->first();

            $start_month = Carbon::parse($payroll_date)->format('Y-m');

            if ($cut_off->day_start > $cut_off->day_end) {
                // When day_start is greater than day_end, start_month should be the previous month
                $start_month = Carbon::parse($start_month . '-01')->subMonth()->format('Y-m');
                $end_month = Carbon::parse($start_month . '-01')->addMonth()->format('Y-m');
            } else {
                // Otherwise, use the current month for both start and end
                $end_month = $start_month;
            }

            // Construct and format day_start and day_end with Y-m-d
            $day_start_date = Carbon::parse($start_month . '-' . $cut_off->day_start);
            $day_end_date = Carbon::parse($end_month . '-' . $cut_off->day_end);

            // Generate array of dates from start to end
            $date_range = [];
            $current_date = $day_start_date->copy();

            while ($current_date->lte($day_end_date)) {
                $date_range[] = $current_date->format('Y-m-d');
                $current_date->addDay(); // Increment by 1 day
            }

            $data[] = [
                'reference_no' => $reference_no,
                'payroll_date' => $payroll_date,
                'cut_off' => $cut_off->cut_off,
                'start_date' => $day_start_date->format('Y-m-d'),
                'end_date' => $day_end_date->format('Y-m-d'),
                'date_range' => $date_range,
                'dealership' => $posetedAttendance->dealership->id
            ];
        }

        return response()->json($data);
    }

    public function getDealerWithAttendancePosting(Request $request)
    {
        // $payroll_date = $request->
        // dd($request->payroll_date);
        DB::enableQueryLog();
        $data = Dealership::with(['attendance_posting' => function ($ap) use ($request) {
                $ap->whereDate('payroll_date', '=', $request->payroll_date);
            }])
            ->whereHas('attendance_posting', function ($query) use ($request) {
                $query->whereDate('payroll_date', '=', $request->payroll_date);
            })
            ->get();

        // dd(DB::getQueryLog());

        return response()->json($data);
    }

    public function getcompanyWithAttendancePosting(Request $request)
    {
        // $payroll_date = $request->
        // dd($request->payroll_date);
        DB::enableQueryLog();
        $data = Company::with(['attendance_posting' => function ($ap) use ($request) {
                $ap->whereDate('payroll_date', '=', $request->payroll_date);
            }])
            ->whereHas('attendance_posting', function ($query) use ($request) {
                $query->whereDate('payroll_date', '=', $request->payroll_date);
            })
            ->get();

        // dd(DB::getQueryLog());

        return response()->json($data);
    }

    public function getEmployeeWithAttendancePosting(Request $request)
    {
        $employee_ids = AttendancePostingDtls::where('reference_no', $request->reference_no)
                ->pluck('employee_id')
                ->toArray();

        $employees = Employee::whereIn('employee_id', $employee_ids)->select('employee_id', 'firstname', 'lastname')->get();

        return response()->json($employees);
        // dd($employees);
    }

    public function getPayrollSummaries()
    {
        $postedAttendances = Payroll::with(['dealership'])->get();

        $data = [];

        foreach($postedAttendances as $posetedAttendance){
            // dd($posetedAttendance->payroll_date);
            $reference_no = $posetedAttendance->reference_no;
            $payroll_date = $posetedAttendance->payroll_date;
            $salary_date = Carbon::parse($payroll_date)->format('j');
            $cut_off = \App\Models\CutoffSetting::where(fn($q) => $q->where('company_id', $posetedAttendance->company_id)->orWhereNull('company_id'))
                ->where('salary_day', $salary_date)
                ->first();

            $start_month = Carbon::parse($payroll_date)->format('Y-m');

            if ($cut_off->day_start > $cut_off->day_end) {
                // When day_start is greater than day_end, start_month should be the previous month
                $start_month = Carbon::parse($start_month . '-01')->subMonth()->format('Y-m');
                $end_month = Carbon::parse($start_month . '-01')->addMonth()->format('Y-m');
            } else {
                // Otherwise, use the current month for both start and end
                $end_month = $start_month;
            }

            // Construct and format day_start and day_end with Y-m-d
            $day_start_date = Carbon::parse($start_month . '-' . $cut_off->day_start);
            $day_end_date = Carbon::parse($end_month . '-' . $cut_off->day_end);

            // Generate array of dates from start to end
            $date_range = [];
            $current_date = $day_start_date->copy();

            while ($current_date->lte($day_end_date)) {
                $date_range[] = $current_date->format('Y-m-d');
                $current_date->addDay(); // Increment by 1 day
            }

            $data[] = [
                'reference_no' => $reference_no,
                'payroll_date' => $payroll_date,
                'cut_off' => $cut_off->cut_off,
                'start_date' => $day_start_date->format('Y-m-d'),
                'end_date' => $day_end_date->format('Y-m-d'),
                'date_range' => $date_range,
                'dealership' => $posetedAttendance->dealership->id
            ];
        }

        return response()->json($data);
    }

    public function getDealerWithPayrollSummary(Request $request)
    {
        // $payroll_date = $request->
        // dd($request->payroll_date);
        DB::enableQueryLog();
        $data = Dealership::with(['payroll' => function ($ap) use ($request) {
                $ap->whereDate('payroll_date', '=', $request->payroll_date);
            }])
            ->whereHas('payroll', function ($query) use ($request) {
                $query->whereDate('payroll_date', '=', $request->payroll_date);
            })
            ->get();

        // dd(DB::getQueryLog());

        return response()->json($data);
    }

    public function getEmployeeWithPayrollSummary(Request $request)
    {
        $employee_ids = PayrollSummary::where('reference_no', $request->reference_no)
                ->pluck('employee_id')
                ->toArray();

        $employees = Employee::whereIn('employee_id', $employee_ids)->select('employee_id', 'firstname', 'lastname')->get();

        return response()->json($employees);
        // dd($employees);
    }

}
