<?php

namespace App\Http\Controllers;

use App\Models\EmployeeAttendance;
use App\Jobs\GenerateAllPayrollReceiptsJob;
use App\Models\Payroll;
use App\Models\Fine;
use App\Models\LeavePackage;
use App\Models\LeaveRequest;
use App\Models\LeaveType;
use App\Models\BaseModel;
use App\Models\Overtime;
use App\Models\PublicHoliday;
use App\Models\RosterAssign;
use App\Models\Salary;
use Barryvdh\DomPDF\Facade\Pdf as PDF;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;


class PayrollController extends Controller
{

    public function generateReceiptsForAllEmployees(Request $request)
    {
        $ids = $this->getCustomerAndWorkspaceIds();
        GenerateAllPayrollReceiptsJob::dispatch(
            $ids['customer_id'],
            $ids['workspace_id'],
            Auth::id()
        );
        return $this->message('Payroll receipts generation has been queued and will be processed in the background.');
    }

    public function index(Request $request)
    {
        $filters = $request->only(['employee', 'pay_year', 'pay_month', 'status']);
        $payrollsQuery = Payroll::with('employee');
        $payrollsQuery = $this->applyCustomerWorkspaceFilter($payrollsQuery);
        $payrollsQuery = $payrollsQuery->when($filters['employee'] ?? null, function ($query, $employeeId) {
            $query->where('employee_id', $employeeId);
        })
            ->when($filters['pay_year'] ?? null, function ($query, $year) {
                $query->where('pay_year', $year);
            })
            ->when($filters['pay_month'] ?? null, function ($query, $month) {
                $query->where('pay_month', $month);
            })
            ->when(isset($filters['status']), function ($query) use ($filters) {
                $query->where('status', $filters['status']);
            })
            ->orderBy('created_at', 'desc');
        // Apply customer and workspace filters
        if($request->filled('pagination')){
            $payrolls = $this->searchFilterRecord( $payrollsQuery, $request);
        }else{
            $payrolls = $payrollsQuery->orderBy('created_at', 'desc')->get();
        }
        // Calculate total fines for each payroll
        $payrolls->transform(function ($payroll) {
            $ids = $this->getCustomerAndWorkspaceIds();
            $totalFines = Fine::where('employee_id', $payroll->employee_id)
                ->where('customer_id', $ids['customer_id'])
                ->where('workspace_id', $ids['workspace_id'])
                ->whereYear('date', $payroll->pay_year)
                ->whereMonth('date', $payroll->pay_month)
                ->where('type', 0)
                ->sum('fine_amount');
            $payroll->total_fines = $totalFines;

            // Convert minutes fields to hours
            $payroll->working_hours = round($payroll->working_hours / 60, 2); // as float, 2 decimals
            $payroll->hours_spent = round($payroll->hours_spent / 60, 2);
            $payroll->actual_working_hours = round(floatval($payroll->actual_working_hours) / 60, 2);
            $payroll->overtime_hours = round(floatval($payroll->overtime_hours) / 60, 2);

            return $payroll;
        });
        return $this->success($payrolls);
    }

    public function fineManagement(Request $request)
    {
        $validator = $this->fineFilterValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $filters = $request->only(['employee', 'fine_amount', 'type', 'date']);
        $finesQuery = Fine::with(['employee']);
        $finesQuery = $this->applyCustomerWorkspaceFilter($finesQuery);
        $finesQuery = $finesQuery->when($filters['employee'] ?? null, function ($query, $employeeId) {
            $query->where('employee_id', $employeeId);
        })
            ->when($filters['fine_amount'] ?? null, function ($query, $amount) {
                $query->where('fine_amount', $amount);
            })
            ->when(isset($filters['type']) && $filters['type'] !== '', function ($query) use ($filters) {
                $query->where('type', $filters['type']);
            })
            ->when($filters['date'] ?? null, function ($query, $date) {
                $query->whereDate('date', $date);
            })
            ->latest('created_at');
        // Apply customer and workspace filters
        if($request->filled('pagination')){
            $finesQuery = $this->searchFilterRecord( $finesQuery, $request);
        }else{
            $finesQuery = $finesQuery->orderBy('created_at', 'desc')->get();
        }
        return $this->success($finesQuery);
    }

    public function fineManagementEdit($id)
    {
        $finesQuery = Fine::query()->with(['employee']);
        $finesQuery = $this->applyCustomerWorkspaceFilter($finesQuery);
        $fine = $finesQuery->find($id);
        if (!$fine) {
            return $this->error('Fine not found', 404);
        }
        return $this->success($fine);
    }

    public function salaryIndex(Request $request)
    {
        $validator = $this->salaryFilterValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $filters = $request->only(['employee', 'from', 'to', 'working_hours', 'basic_salary']);
        $salariesQuery = Salary::with('employee');
        $salariesQuery = $this->applyCustomerWorkspaceFilter($salariesQuery);
        $salariesQuery = $salariesQuery->when($filters['employee'] ?? null, function ($query, $employeeId) {
            $query->where('employee_id', $employeeId);
        })
        ->when($filters['from'] ?? null, function ($query, $from) {
            $query->where('from', $from);
        })
        ->when($filters['to'] ?? null, function ($query, $to) {
            $query->where('to', $to);
        })
        ->when($filters['working_hours'] ?? null, function ($query, $workingHours) {
            $query->where('working_hours', $workingHours);
        })
        ->when($filters['basic_salary'] ?? null, function ($query, $basicSalary) {
            $query->where('basic_salary', $basicSalary);
        })
        ->orderBy('created_at', 'desc');
        return $this->success($salariesQuery);
    }

    public function salaryEdit($id)
    {
        $salary = Salary::query();
        $salary = $this->applyCustomerWorkspaceFilter($salary);
        $salary = $salary->with('employee')->find($id);
        if (!$salary) {
            return $this->error('Salary not found', 404);
        }
        return $this->success($salary);
    }

    public function store(Request $request)
    {
        $validator = $this->payrollStoreValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $validatedData = $validator->validated();
        $payroll = Payroll::create([
            'employee_id' => $validatedData['employee_id'],
            'pay_year' => $validatedData['pay_year'],
            'pay_month' => $validatedData['pay_month'],
            'basic_salary' => $validatedData['basic_salary'],
            'working_hours' => $validatedData['working_hours'] ?? 0,
            'hours_spent' => $validatedData['hours_spent'] ?? 0,
            'calculated_salary' => $validatedData['calculated_salary'],
            'status' => 0,
            'customer_id' => $ids['customer_id'],
            'workspace_id' => $ids['workspace_id'],
            'created_by' => Auth::id(),
        ]);
        return $this->success($payroll, 'Payroll created successfully!');
    }

    public function fineStore(Request $request)
    {
        $validator = $this->fineStoreValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $validatedData = $validator->validated();
        $fine = Fine::create([
            'employee_id' => $validatedData['employee_id'],
            'date' => $validatedData['fine_date'],
            'fine_amount' => $validatedData['fine_amount'],
            'type' => $validatedData['type'],
            'fine_reason' => $validatedData['fine_reason'],
            'customer_id' => $ids['customer_id'],
            'workspace_id' => $ids['workspace_id'],
            'created_by' => Auth::id(),
        ]);
        return $this->success($fine, 'Bonus created successfully!');
    }

    /**
     * Create a new salary
     */
    public function salaryStore(Request $request)
    {
        $validator = $this->salaryStoreValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $validatedData = $validator->validated();

        // Check for exact duplicate (same employee, dates, and salary)
        $existingDuplicate = Salary::where('employee_id', $validatedData['employee_id'])
            ->where('from', $validatedData['from'])
            ->where('to', $validatedData['to'])
            ->where('basic_salary', $validatedData['basic_salary'])
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->first();

        if ($existingDuplicate) {
            return $this->error('A salary record with the same period and amount already exists for this employee.', 422);
        }

        // Check for overlapping salary periods
        $overlappingSalary = Salary::where('employee_id', $validatedData['employee_id'])
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where(function ($query) use ($validatedData) {
                // Check if new period overlaps with existing periods
                $query->where(function ($q) use ($validatedData) {
                    // New period starts within existing period
                    $q->where('from', '<=', $validatedData['from'])
                        ->where('to', '>=', $validatedData['from']);
                })->orWhere(function ($q) use ($validatedData) {
                    // New period ends within existing period
                    $q->where('from', '<=', $validatedData['to'])
                        ->where('to', '>=', $validatedData['to']);
                })->orWhere(function ($q) use ($validatedData) {
                    // New period completely contains existing period
                    $q->where('from', '>=', $validatedData['from'])
                        ->where('to', '<=', $validatedData['to']);
                });
            })
            ->first();

        if ($overlappingSalary) {
            $overlappingPeriod = date('M d, Y', strtotime($overlappingSalary->from)) . ' - ' . date('M d, Y', strtotime($overlappingSalary->to));
            return $this->error("The salary period overlaps with an existing salary period ({$overlappingPeriod}) for this employee.", 422);
        }

        $salary = Salary::create([
            'employee_id' => $validatedData['employee_id'],
            'from' => $validatedData['from'],
            'to' => $validatedData['to'],
            'basic_salary' => $validatedData['basic_salary'],
            'working_hours' => $validatedData['working_hours'] ?? 0,
            'customer_id' => $ids['customer_id'],
            'workspace_id' => $ids['workspace_id'],
            'created_by' => Auth::id(),
        ]);
        return $this->success($salary, 'Salary created successfully!');
    }

    /**
     * Show payroll details
     */
    public function show($id)
    {
        $payrollQuery = Payroll::query();
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);
        $payroll = $payrollQuery->find($id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }
        return $this->success($payroll);
    }

    /**
     * Update payroll
     */
    public function update(Request $request)
    {
        $validator = $this->payrollUpdateValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $payrollQuery = Payroll::query();
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);
        $payroll = $payrollQuery->find($request->id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }
        $validatedData = $validator->validated();
        // Parse hours_spent (HH:MM or decimal)
        $hoursSpent = 0;
        if (strpos($validatedData['hours_spent'], ':') !== false) {
            [$hours, $minutes] = explode(':', $validatedData['hours_spent']);
            $hoursSpent = ((int)$hours * 60) + (int)$minutes;
        } else {
            $hoursSpent = (float)$validatedData['hours_spent'] * 60;
        }
        $workingHours = (float)$validatedData['working_hours'] * 60;
        $payroll->update([
            'employee_id' => $validatedData['employee_id'],
            'pay_year' => $validatedData['pay_year'],
            'pay_month' => $validatedData['pay_month'],
            'basic_salary' => $validatedData['basic_salary'],
            'working_hours' => $workingHours ?? 0,
            'hours_spent' => $hoursSpent ?? 0,
            'calculated_salary' => $validatedData['calculated_salary'],
            'status' => 0,
        ]);
        return $this->success($payroll, 'Payroll updated successfully!');
    }

    /**
     * Update fine
     */
    public function fineUpdate(Request $request)
    {
        $validator = $this->fineUpdateValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $fineQuery = Fine::query();
        $fineQuery = $this->applyCustomerWorkspaceFilter($fineQuery);
        $fine = $fineQuery->find($request->id);
        if (!$fine) {
            return $this->error('Fine not found', 404);
        }
        $validatedData = $validator->validated();
        $fine->update([
            'employee_id' => $validatedData['employee_id'],
            'date' => $validatedData['fine_date'],
            'type' => $validatedData['type'],
            'fine_amount' => $validatedData['fine_amount'],
            'fine_reason' => $validatedData['fine_reason'],
        ]);
        return $this->success($fine, 'Bonus updated successfully!');
    }

    /**
     * Update salary
     */
    public function salaryUpdate(Request $request)
    {
        $validator = $this->salaryUpdateValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $salaryQuery = Salary::query();
        $salaryQuery = $this->applyCustomerWorkspaceFilter($salaryQuery);
        $salary = $salaryQuery->find($request->id);
        if (!$salary) {
            return $this->error('Salary not found', 404);
        }
        $validatedData = $validator->validated();

        // Check for exact duplicate (excluding current record)
        $existingDuplicate = Salary::where('employee_id', $validatedData['employee_id'])
            ->where('from', $validatedData['from'])
            ->where('to', $validatedData['to'])
            ->where('basic_salary', $validatedData['basic_salary'])
            ->where('customer_id', $salary->customer_id)
            ->where('workspace_id', $salary->workspace_id)
            ->where('id', '!=', $request->id) // Exclude current record
            ->first();

        if ($existingDuplicate) {
            return $this->error('A salary record with the same period and amount already exists for this employee.', 422);
        }

        // Check for overlapping salary periods (excluding current record)
        $overlappingSalary = Salary::where('employee_id', $validatedData['employee_id'])
            ->where('customer_id', $salary->customer_id)
            ->where('workspace_id', $salary->workspace_id)
            ->where('id', '!=', $request->id) // Exclude current record
            ->where(function ($query) use ($validatedData) {
                // Check if new period overlaps with existing periods
                $query->where(function ($q) use ($validatedData) {
                    // New period starts within existing period
                    $q->where('from', '<=', $validatedData['from'])
                        ->where('to', '>=', $validatedData['from']);
                })->orWhere(function ($q) use ($validatedData) {
                    // New period ends within existing period
                    $q->where('from', '<=', $validatedData['to'])
                        ->where('to', '>=', $validatedData['to']);
                })->orWhere(function ($q) use ($validatedData) {
                    // New period completely contains existing period
                    $q->where('from', '>=', $validatedData['from'])
                        ->where('to', '<=', $validatedData['to']);
                });
            })
            ->first();

        if ($overlappingSalary) {
            $overlappingPeriod = date('M d, Y', strtotime($overlappingSalary->from)) . ' - ' . date('M d, Y', strtotime($overlappingSalary->to));
            return $this->error("The salary period overlaps with an existing salary period ({$overlappingPeriod}) for this employee.", 422);
        }
        $salary->update([
            'employee_id' => $validatedData['employee_id'],
            'from' => $validatedData['from'],
            'to' => $validatedData['to'],
            'basic_salary' => $validatedData['basic_salary'],
            'working_hours' => $validatedData['working_hours'] ?? 0,
        ]);
        return $this->success($salary, 'Salary updated successfully!');
    }

    /**
     * Toggle payroll status
     */
    public function toggleStatus(Request $request)
    {
        $payrollQuery = Payroll::query();
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);
        $payroll = $payrollQuery->find($request->payroll_id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }
        $payroll->status = $payroll->status == 1 ? 2 : 1;
        $payroll->save();
        return $this->success($payroll, 'Status updated successfully!');
    }

    /**
     * Delete payroll
     */
    public function destroy($id)
    {
        $payrollQuery = Payroll::query();
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);
        $payroll = $payrollQuery->find($id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        Fine::where('employee_id', $payroll->employee_id)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where('type', 0)
            ->whereYear('date', $payroll->pay_year)
            ->whereMonth('date', $payroll->pay_month)
            ->delete();
        $payroll->delete();
        return $this->message('Payroll deleted successfully.');
    }

    /**
     * Delete fine
     */
    public function fineDestroy($id)
    {
        $fineQuery = Fine::query();
        $fineQuery = $this->applyCustomerWorkspaceFilter($fineQuery);
        $fine = $fineQuery->find($id);
        if (!$fine) {
            return $this->error('Fine not found', 404);
        }
        $fine->delete();
        return $this->message('Bonus deleted successfully.');
    }

    /**
     * Delete salary
     */
    public function salaryDestroy($id)
    {
        $salaryQuery = Salary::query();
        $salaryQuery = $this->applyCustomerWorkspaceFilter($salaryQuery);
        $salary = $salaryQuery->find($id);
        if (!$salary) {
            return $this->error('Salary not found', 404);
        }
        $salary->delete();
        return $this->message('Salary deleted successfully.');
    }

    /**
     * Get employee fines for date range
     */
    public function getEmployeeFines(Request $request)
    {
        $validator = $this->employeeFinesValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $validated = $validator->validated();
        $employeeId = Auth::user()->id;
        $ids = $this->getCustomerAndWorkspaceIds();
        $fines = Fine::where('employee_id', $employeeId)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->whereBetween('date', [$validated['from'], $validated['to']])
            ->get();
        return $this->success($fines);
    }

    /**
     * Get payroll receipt details
     */
    public function getPayrollReceipt($id)
    {
        $payrollQuery = Payroll::query();
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);
        $payroll = $payrollQuery->find($id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }
        $payrollDetails = $this->getPayrollDetails($id);
        return $this->success($payrollDetails);
    }

    /**
     * Select employees for payslip generation
     */
    public function selectEmployeesForPayslip(Request $request)
    {
        $validator = $this->selectEmployeesValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $monthFilter = $request->input('month') ?? now()->format('Y-m');
        $employeeSearch = $request->input('employee_search', '');

        $monthDate = BaseModel::safeCarbonParse($monthFilter . '-01', 'PayrollController.selectEmployeesForPayslip.month');
        if (!($monthDate instanceof Carbon)) {
            return $this->error('Invalid month supplied. Please use format YYYY-MM.', 422);
        }

        $date = $monthDate->copy()->startOfMonth();
        $year = $date->year;
        $month = $date->month;
        $employees = $this->getEmployees();
        // Filter by employee name if search is provided
        if (!empty($employeeSearch)) {
            $employees = $employees->filter(function ($employee) use ($employeeSearch) {
                $fullName = trim(($employee->EmpPersonalDetails->first_name ?? '') . ' ' .
                    ($employee->EmpPersonalDetails->middle_name ?? '') . ' ' .
                    ($employee->EmpPersonalDetails->last_name ?? ''));
                return stripos($fullName, $employeeSearch) !== false;
            });
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $payrolls = Payroll::where('pay_year', $year)
            ->where('pay_month', $month)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->get()
            ->keyBy('employee_id');

        $employees = $employees->map(function ($employee) use ($payrolls, $year, $month, $ids) {
            $employee->payslip = $payrolls->get($employee->id);

            // Create the target date for the selected month (using first day of month)
            $targetDate = sprintf('%04d-%02d-01', $year, $month);

            // Find salary for the selected month
            $salary = Salary::where('employee_id', $employee->id)
                ->where('customer_id', $ids['customer_id'])
                ->where('workspace_id', $ids['workspace_id'])
                ->where('from', '<=', $targetDate)
                ->where('to', '>=', $targetDate)
                ->first();

            $employee->salary = $salary;
            return $employee;
        });

        return $this->successWithNestedPagination([
            'employees' => $employees,
            'monthFilter' => $monthFilter
        ]);
    }

    /**
     * Generate payslips for selected employees
     */
    public function generateSelectedSlips(Request $request)
    {
        return $this->generatePayslipsForEmployees($request);
    }

    /**
     * Bulk delete payslips
     */
    public function bulkDelete(Request $request)
    {
        $validator = $this->bulkDeleteValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $employeeIds = $request->employee_ids;
        $month = $request->month;
        $bulkMonthDate = BaseModel::safeCarbonParse($month . '-01', 'PayrollController.bulkDelete.month');
        if (!($bulkMonthDate instanceof Carbon)) {
            return $this->error('Invalid month supplied. Please use format YYYY-MM.', 422);
        }

        $date = $bulkMonthDate->copy()->startOfMonth();
        $year = $date->year;
        $monthNum = $date->month;
        $ids = $this->getCustomerAndWorkspaceIds();
        $deletedCount = 0;
        $foundEmployees = [];
        $notFoundEmployees = [];
        foreach ($employeeIds as $employeeId) {
            $payroll = Payroll::where('employee_id', $employeeId)
                ->where('pay_year', $year)
                ->where('pay_month', $monthNum)
                ->where('customer_id', $ids['customer_id'])
                ->where('workspace_id', $ids['workspace_id'])
                ->first();
            if ($payroll) {
                // Delete related fines for this payroll period
                Fine::where('employee_id', $employeeId)
                    ->where('customer_id', $ids['customer_id'])
                    ->where('workspace_id', $ids['workspace_id'])
                    ->whereYear('date', $year)
                    ->whereMonth('date', $monthNum)
                    ->delete();
                $payroll->delete();
                $deletedCount++;
                $foundEmployees[] = $employeeId;
            } else {
                $notFoundEmployees[] = $employeeId;
            }
        }
        // If no employees were found at all
        if ($deletedCount === 0) {
            return $this->error('No employees found with payslips for the selected month.', 404);
        }
        // If some employees were found and some not
        if (!empty($notFoundEmployees)) {
            $message = "$deletedCount payslip(s) deleted successfully.";
            if (count($notFoundEmployees) === 1) {
                $message .= " Employee with ID " . $notFoundEmployees[0] . " was not found.";
            } else {
                $message .= " Employees with IDs " . implode(', ', $notFoundEmployees) . " were not found.";
            }
            return $this->success([
                'deleted_count' => $deletedCount,
                'found_employees' => $foundEmployees,
                'not_found_employees' => $notFoundEmployees
            ], $message);
        }
        // All employees were found and deleted
        return $this->success([
            'deleted_count' => $deletedCount,
            'found_employees' => $foundEmployees,
            'not_found_employees' => []
        ], "$deletedCount payslip(s) deleted successfully.");
    }

    /**
     * Get roster working hours for employee
     */
    public function getRosterWorkingHours(Request $request)
    {
        $validator = $this->rosterWorkingHoursValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $employeeId = $request->employee_id;
        $year = $request->year;
        $month = $request->month;

        $workingRangeStart = BaseModel::safeCarbonParse(sprintf('%04d-%02d-01', $year, $month), 'PayrollController.getRosterWorkingHours.start');
        if (!($workingRangeStart instanceof Carbon)) {
            return $this->error('Invalid month supplied. Please use a valid year-month combination.', 422);
        }

        // Create date range for the month
        $startDate = $workingRangeStart->copy()->startOfMonth()->toDateString();
        $endDate = $workingRangeStart->copy()->endOfMonth()->toDateString();
        $workingMinutes = $this->calculateScheduledWorkingHours($employeeId, $startDate, $endDate);
        return $this->success([
            'working_hours' => $workingMinutes,
            'working_hours_formatted' => round($workingMinutes / 60, 2)
        ]);
    }

    /**
     * View payroll details
     */
    public function viewDetails($id)
    {
        $payrollQuery = Payroll::query();
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);
        $payroll = $payrollQuery->find($id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }
        $payrollDetails = $this->getPayrollDetails($id);
        return $this->success($payrollDetails);
    }

    /**
     * Save payroll PDF
     */
    public function savePayrollPdf(Request $request, $id)
    {
        $validator = $this->savePayrollPdfValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }

        $payrollQuery = Payroll::with(['employee', 'employeCom']);
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);
        $payroll = $payrollQuery->find($id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }
        try {
            if ($request->hasFile('pdf')) {
                $file = $request->file('pdf');
                // Get month and year folder name
                $monthName = date('F', mktime(0, 0, 0, $payroll->pay_month, 10));
                $folderName = $monthName . '_' . $payroll->pay_year;
                $directoryPath = public_path('payroll_receipts/' . $folderName);

                // Create directory if it doesn't exist
                if (!is_dir($directoryPath)) {
                    mkdir($directoryPath, 0755, true);
                }

                // Get employee name for filename
                $emp = $payroll->employee ?? null;
                $empName = '';
                if ($emp) {
                    $empName = trim(($emp->first_name ?? '') . '_' . ($emp->middle_name ?? '') . '_' . ($emp->last_name ?? ''));
                    $empName = preg_replace('/[^A-Za-z0-9_]/', '', str_replace(' ', '_', $empName));
                    $empName = preg_replace('/_+/', '_', $empName);
                } else {
                    $empName = 'Employee_' . $payroll->employee_id;
                }
                $filename = "{$empName}_{$payroll->pay_year}_{$payroll->pay_month}.pdf";
                $filePath = $directoryPath . '/' . $filename;

                // Move uploaded file to destination
                if (!$file->move($directoryPath, $filename)) {
                    throw new \Exception('Failed to save PDF file');
                }

                // Update payroll record with PDF path
                $payroll->update([
                    'pdf_path' => 'payroll_receipts/' . $folderName . '/' . $filename
                ]);
                return $this->success([
                    'pdf_path' => 'payroll_receipts/' . $folderName . '/' . $filename
                ], 'PDF saved successfully');
            }
            return $this->error('No PDF file uploaded', 400);
        } catch (\Exception $e) {
            Log::error('Error saving payroll PDF: ' . $e->getMessage());
            return $this->error('Error saving PDF: ' . $e->getMessage(), 500);
        }
    }
    public function savePayrollAtt(Request $request, $id)
    {
        $validator = $this->savePayrollPdfValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }

        $payrollQuery = Payroll::with(['employee', 'employeCom']);
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);

        $payroll = $payrollQuery->find($id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }

        try {
            if ($request->hasFile('pdf')) {
                $file = $request->file('pdf');

                // Get month and year folder name
                $monthName = date('F', mktime(0, 0, 0, $payroll->pay_month, 10));
                $folderName = $monthName . '_' . $payroll->pay_year;
                $directoryPath = public_path('attendance_pdfs/' . $folderName);

                // Create directory if it doesn't exist
                if (!is_dir($directoryPath)) {
                    mkdir($directoryPath, 0755, true);
                }

                // Get employee name for filename
                $emp = $payroll->employee ?? null;
                $empName = '';
                if ($emp) {
                    $empName = trim(($emp->first_name ?? '') . '_' . ($emp->middle_name ?? '') . '_' . ($emp->last_name ?? ''));
                    $empName = preg_replace('/[^A-Za-z0-9_]/', '', str_replace(' ', '_', $empName));
                    $empName = preg_replace('/_+/', '_', $empName);
                } else {
                    $empName = 'Employee_' . $payroll->employee_id;
                }

                $filename = "{$empName}_{$payroll->pay_year}_{$payroll->pay_month}.pdf";
                $filePath = $directoryPath . '/' . $filename;

                // Move uploaded file to destination
                if (!$file->move($directoryPath, $filename)) {
                    throw new \Exception('Failed to save attendance PDF file');
                }

                // Update payroll record with PDF path
                $payroll->update([
                    'attendance_pdf_path' => 'attendance_pdfs/' . $folderName . '/' . $filename
                ]);

                return $this->success([
                    'attendance_pdf_path' => 'attendance_pdfs/' . $folderName . '/' . $filename
                ], 'PDF saved successfully');
            }

            return $this->error('No PDF file uploaded', 400);
        } catch (\Exception $e) {
            Log::error('Error saving payroll PDF: ' . $e->getMessage());
            return $this->error('Error saving PDF: ' . $e->getMessage(), 500);
        }
    }
    /**
     * Download payroll PDF
     */
    public function downloadPayrollPdf($id)
    {
        $payrollQuery = Payroll::query();
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);
        $payroll = $payrollQuery->find($id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }
        if (!$payroll->pdf_path) {
            return $this->error('PDF not found', 404);
        }
        $filepath = storage_path('app/public/' . $payroll->pdf_path);
        if (!file_exists($filepath)) {
            return $this->error('PDF file not found on server', 404);
        }

        // Pass headers as the third argument to download()
        return response()->download($filepath, null, [
            'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0',
            'Pragma' => 'no-cache',
            'Expires' => 'Sat, 01 Jan 2000 00:00:00 GMT',
        ]);
    }

    public function downloadAttendancePdf($id)
    {
        $payrollQuery = Payroll::query();
        $payrollQuery = $this->applyCustomerWorkspaceFilter($payrollQuery);
        $payroll = $payrollQuery->find($id);
        if (!$payroll) {
            return $this->error('Payroll not found', 404);
        }

        if (!$payroll->attendance_pdf_path) {
            return $this->error('PDF not found', 404);
        }

        $filepath = storage_path('app/public/' . $payroll->attendance_pdf_path);

        if (!file_exists($filepath)) {
            return $this->error('PDF file not found on server', 404);
        }

        // Pass headers as the third argument to download()
        return response()->download($filepath, null, [
            'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0',
            'Pragma' => 'no-cache',
            'Expires' => 'Sat, 01 Jan 2000 00:00:00 GMT',
        ]);
    }

    /**
     * Generate attendance PDF
     */
    public function attendancePdf($id)
    {
        try {
            $ids = $this->getCustomerAndWorkspaceIds();
            $payroll = Payroll::with(['employee', 'employeCom'])->findOrFail($id);

            // Convert minutes fields to hours
            $payroll->working_hours = round($payroll->working_hours / 60, 2);
            $payroll->hours_spent = round($payroll->hours_spent / 60, 2);
            $payroll->actual_working_hours = round(floatval($payroll->actual_working_hours) / 60, 2);
            $payroll->overtime_hours = round(floatval($payroll->overtime_hours) / 60, 2);

            $periodBase = BaseModel::safeCarbonParse(sprintf('%04d-%02d-01', $payroll->pay_year, $payroll->pay_month), 'PayrollController.attendancePdf.period');
            if (!($periodBase instanceof Carbon)) {
                return $this->error('Invalid payroll period detected for this record.', 500);
            }

            $startDate = $periodBase->copy()->startOfMonth();
            $endDate = $periodBase->copy()->endOfMonth();

            $fines = Fine::where('employee_id', $payroll->employee_id)->where('type', 1)
                ->whereYear('date', $payroll->pay_year)
                ->whereMonth('date', $payroll->pay_month)
                ->orderBy('date', 'desc')
                ->get();

            $overtimeRecords = Overtime::where('employee_id', $payroll->employee_id)
                ->where('status', 1)
                ->whereBetween('date', [$startDate->toDateString(), $endDate->toDateString()])
                ->orderBy('date', 'desc')
                ->get();

            $salary = Salary::where('employee_id', $payroll->employee_id)
                ->where(function ($query) use ($startDate, $endDate) {
                    $query->where('from', '<=', $endDate->format('Y-m'))
                        ->where('to', '>=', $startDate->format('Y-m'));
                })
                ->first();

            // $totalFines = $fines->where('type', 0)->sum('fine_amount');
            $totalBonuses = $fines->where('type', 1)->sum('fine_amount');
            $adjustedSalary = $payroll->calculated_salary  + $totalBonuses;
            // $adjustedSalary = $payroll->calculated_salary - $totalFines + $totalBonuses;

            $holidays = PublicHoliday::where('customer_id', $ids['customer_id'])
                ->where('workspace_id', $ids['workspace_id'])->where(function ($query) use ($startDate, $endDate) {
                    $query->whereBetween('from', [$startDate, $endDate])
                        ->orWhereBetween('to', [$startDate, $endDate])
                        ->orWhere(function ($q) use ($startDate, $endDate) {
                            $q->where('from', '<', $startDate)
                                ->where('to', '>', $endDate);
                        });
                })->get();

            $holidayDates = [];
            foreach ($holidays as $holiday) {
                $holidayFrom = BaseModel::safeCarbonParse($holiday->from, 'PayrollController.attendancePdf.holiday.from');
                $holidayTo = BaseModel::safeCarbonParse($holiday->to, 'PayrollController.attendancePdf.holiday.to');

                if (!($holidayFrom instanceof Carbon) || !($holidayTo instanceof Carbon)) {
                    Log::warning("Skipping holiday {$holiday->id} due to unparseable dates", [
                        'from' => $holiday->from,
                        'to' => $holiday->to,
                        'context' => 'attendancePdf'
                    ]);
                    continue;
                }

                $clampedFrom = $holidayFrom->greaterThan($startDate) ? $holidayFrom->copy() : $startDate->copy();
                $clampedTo = $holidayTo->lessThan($endDate) ? $holidayTo->copy() : $endDate->copy();

                if ($clampedFrom->gt($clampedTo)) {
                    continue;
                }

                $period = CarbonPeriod::create($clampedFrom, $clampedTo);
                foreach ($period as $datePeriod) {
                    $holidayDates[] = $datePeriod->format('Y-m-d');
                }
            }
            $holidayDates = array_unique($holidayDates);

            $scheduledDays = RosterAssign::where('assign_to', $payroll->employee_id)
                ->whereBetween('schedule_date', [$startDate->toDateString(), $endDate->toDateString()])
                ->pluck('schedule_date')
                ->toArray();

            $actualWorkingDays = array_diff($scheduledDays, $holidayDates);
            $totalWorkingDays = count($actualWorkingDays);
            $totalHolidays = count($holidayDates);
            $ymdDate = array_map(function ($date) {
                $parsed = BaseModel::safeCarbonParse($date, 'PayrollController.attendancePdf.actualWorkingDays');
                if (!($parsed instanceof Carbon)) {
                    Log::warning('Skipping unparsable working day when normalizing', [
                        'date' => $date,
                        'context' => 'attendancePdf.actualWorkingDays',
                    ]);
                    return null;
                }
                return $parsed->format('Y-m-d');
            }, $actualWorkingDays);
            $ymdDate = array_filter($ymdDate);
            $presentDays = EmployeeAttendance::where('employee_id', $payroll->employee_id)
                ->whereIn('date', $ymdDate)
                ->whereNotNull('check_in')
                ->whereNotNull('check_out')
                ->distinct('date')
                ->count('date');

            $absentDays = $totalWorkingDays - $presentDays;
            $attendanceRate = $totalWorkingDays > 0 ? round(($presentDays / $totalWorkingDays) * 100, 1) : 0;

            $attendances = EmployeeAttendance::where('employee_id', $payroll->employee_id)
                ->whereBetween('date', [$startDate->toDateString(), $endDate->toDateString()])
                ->orderBy('date', 'desc')
                ->get();

            // Calculate approved leave days and hours for this payroll period (with paid/unpaid separation)
            $approvedLeaves = LeaveRequest::where('employee_id', $payroll->employee_id)
                ->where('status', 1) // Only approved
                ->where('customer_id', $ids['customer_id'])
                ->where('workspace_id', $ids['workspace_id'])
                ->where(function ($query) use ($startDate, $endDate) {
                    $query->whereBetween('from', [$startDate->toDateString(), $endDate->toDateString()])
                        ->orWhereBetween('to', [$startDate->toDateString(), $endDate->toDateString()])
                        ->orWhere(function ($q) use ($startDate, $endDate) {
                            $q->where('from', '<', $startDate->toDateString())
                                ->where('to', '>', $endDate->toDateString());
                        });
                })
                ->get();

            // Initialize all leave counters
            $approvedLeave = 0; // Keep existing for backward compatibility
            $approvedLeaveHours = 0; // Keep existing for backward compatibility
            $paidLeaveDays = 0;
            $paidLeaveHours = 0;
            $unpaidLeaveDays = 0;
            $unpaidLeaveHours = 0;

            foreach ($approvedLeaves as $leave) {
                try {
                    $leavePackage = LeavePackage::find($leave->leave_package_id);
                    if (!$leavePackage) {
                        Log::warning("Leave package not found for leave ID: {$leave->id} in attendancePdf");
                        continue;
                    }
                    
                    if (!$leavePackage->leave_type_id) {
                        Log::warning("Leave package {$leavePackage->id} has no leave_type_id in attendancePdf");
                        continue;
                    }
                    
                    $leaveType = LeaveType::find($leavePackage->leave_type_id);
                    if (!$leaveType || !$leaveType->leave_hours || $leaveType->leave_hours <= 0) {
                        Log::warning("Invalid leave type for leave_type_id: {$leavePackage->leave_type_id} in attendancePdf");
                        continue;
                    }
                    
                    $leaveFromParsed = \App\Models\BaseModel::safeCarbonParse($leave->from, 'attendancePdf leave from');
                    $leaveToParsed = \App\Models\BaseModel::safeCarbonParse($leave->to, 'attendancePdf leave to');
                    
                    if (!($leaveFromParsed instanceof \Carbon\Carbon) || !($leaveToParsed instanceof \Carbon\Carbon)) {
                        Log::warning("Failed to parse leave dates for leave ID: {$leave->id} in attendancePdf");
                        continue;
                    }
                    
                    $from = $leaveFromParsed->greaterThan($startDate) ? $leaveFromParsed : $startDate;
                    $to = $leaveToParsed->lessThan($endDate) ? $leaveToParsed : $endDate;
                    $days = $from->diffInDays($to) + 1;
                    
                    if ($days <= 0) {
                        continue; // Leave outside the period
                    }
                    
                    $hours = $days * $leaveType->leave_hours;
                    
                    // Keep existing totals for backward compatibility
                    $approvedLeave += $days;
                    $approvedLeaveHours += $hours;
                    
                    // Separate paid and unpaid leave with safe checking
                    $isPaid = isset($leavePackage->is_paid) ? (int)$leavePackage->is_paid : 0;
                    
                    if ($isPaid === 1) {
                        $paidLeaveDays += $days;
                        $paidLeaveHours += $hours;
                    } else {
                        $unpaidLeaveDays += $days;
                        $unpaidLeaveHours += $hours;
                    }
                    
                } catch (\Exception $e) {
                    Log::error("Error processing leave ID: {$leave->id} in attendancePdf: " . $e->getMessage());
                    continue; // Skip this leave and continue
                }
            }
            $data = [
                'payroll' => $payroll,
                'salary' => $salary,
                'fines' => $fines,
                // 'totalFines' => $totalFines,
                'totalBonuses' => $totalBonuses,
                'adjustedSalary' => $adjustedSalary,
                'overtimeRecords' => $overtimeRecords,
                'holidays' => $holidays,
                'holidayDates' => $holidayDates,
                'totalHolidays' => $totalHolidays,
                'scheduledDays' => $scheduledDays,
                'actualWorkingDays' => array_values($actualWorkingDays),
                'totalWorkingDays' => $totalWorkingDays,
                'presentDays' => $presentDays,
                'absentDays' => $absentDays,
                'attendanceRate' => $attendanceRate,
                'attendances' => $attendances,
                'approvedLeave' => $approvedLeave, // Keep existing for backward compatibility
                'approvedLeaveHours' => $approvedLeaveHours, // Keep existing for backward compatibility
                'approvedLeaves' => $approvedLeaves,
                // New paid/unpaid leave breakdown
                'paidLeaveDays' => $paidLeaveDays,
                'paidLeaveHours' => $paidLeaveHours,
                'unpaidLeaveDays' => $unpaidLeaveDays,
                'unpaidLeaveHours' => $unpaidLeaveHours,
                'totalLeaveDays' => $paidLeaveDays + $unpaidLeaveDays,
                'totalLeaveHours' => $paidLeaveHours + $unpaidLeaveHours,
            ];
            return $this->success($data);
        } catch (\Exception $e) {
            Log::error('Error generating attendance PDF: ' . $e->getMessage());
            return $this->error('Error generating PDF: ' . $e->getMessage(), 500);
        }
    }

    /**
     * Send receipt email
     */
    public function sendReceiptEmail(Request $request)
    {
        return $this->sendPayslipEmail($request->id);
    }
}
