<?php

namespace App\Http\Controllers\Modules\Employee;

use App\Http\Controllers\Controller;
use App\Http\Traits\HasRoleViews;
use App\Models\EmployeeDailyReport;
use App\Models\EmployeeDetails;
use App\Models\EmployeeAttendance;
use App\Models\EmployeeLeave;
use App\Models\Holiday;
use App\Services\Employee\DailyReportService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use PhpOffice\PhpSpreadsheet\IOFactory;
use Carbon\Carbon;

/**
 * Unified Employee Daily Report Controller
 *
 * Handles daily report management, attendance calendar view.
 * Reference: SuperAdminEmployeeDailyReportController
 */
class EmployeeDailyReportController extends Controller
{
    use HasRoleViews;

    protected DailyReportService $dailyReportService;

    public function __construct(DailyReportService $dailyReportService)
    {
        $this->dailyReportService = $dailyReportService;
    }

    /**
     * Display the daily report page with filtering
     */
    public function index(Request $request)
    {
        $date = $request->input('date', date('Y-m-d'));
        $department = $request->input('department');
        $employeeId = $request->input('employee_id');

        $employeesQuery = EmployeeDetails::query();

        if ($department) {
            $employeesQuery->where('department', $department);
        }

        if ($employeeId) {
            $employeesQuery->where('id', $employeeId);
        }

        $employees = $employeesQuery->orderBy('employee_id')->get();
        $employeeCodes = $employees->pluck('employee_id');
        $employeeIds = $employees->pluck('id');

        $departments = EmployeeDetails::distinct()
            ->pluck('department')
            ->filter()
            ->values();

        $attendanceData = EmployeeAttendance::whereDate('date', $date)
            ->whereIn('employee_id', $employeeCodes)
            ->get()
            ->keyBy('employee_id');

        $leaveData = EmployeeLeave::whereDate('leave_from', '<=', $date)
            ->whereDate('leave_to', '>=', $date)
            ->whereIn('employee_id', $employeeIds)
            ->get()
            ->keyBy('employee_id');

        return $this->roleView('employee.employeedailyreport', [
            'attendanceData' => $attendanceData,
            'leaveData' => $leaveData,
            'employees' => $employees,
            'departments' => $departments,
            'date' => $date
        ]);
    }

    /**
     * Store a new daily report
     */
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate($this->getValidationRules());

        try {
            $report = $this->dailyReportService->createOrUpdate($validated);
            return response()->json([
                'status' => 'success',
                'message' => 'Daily report created successfully',
                'data' => $report,
            ]);
        } catch (\Exception $e) {
            Log::error('Failed to create daily report: ' . $e->getMessage());
            return response()->json([
                'status' => 'error',
                'message' => 'Failed to create daily report',
            ], 500);
        }
    }

    /**
     * Get report for editing
     */
    public function edit(int $id): JsonResponse
    {
        $report = $this->dailyReportService->findByIdOrFail($id);
        return response()->json($report);
    }

    /**
     * Update a daily report
     */
    public function update(Request $request, int $id): JsonResponse
    {
        $validated = $request->validate($this->getValidationRules());

        try {
            $report = $this->dailyReportService->update($id, $validated);
            return response()->json([
                'status' => 'updated',
                'message' => 'Daily report updated successfully',
                'data' => $report,
            ]);
        } catch (\Exception $e) {
            Log::error('Failed to update daily report: ' . $e->getMessage());
            return response()->json([
                'status' => 'error',
                'message' => 'Failed to update daily report',
            ], 500);
        }
    }

    /**
     * Delete a daily report
     */
    public function destroy(int $id): JsonResponse
    {
        try {
            $this->dailyReportService->delete($id);
            return response()->json([
                'status' => 'deleted',
                'message' => 'Daily report deleted successfully',
            ]);
        } catch (\Exception $e) {
            Log::error('Failed to delete daily report: ' . $e->getMessage());
            return response()->json([
                'status' => 'error',
                'message' => 'Failed to delete daily report',
            ], 500);
        }
    }

    /**
     * Get validation rules for daily report
     */
    protected function getValidationRules(): array
    {
        return [
            'employee_id' => 'required|string|max:50',
            'date' => 'required|date',
            'shift' => 'nullable|string|in:' . implode(',', [
                EmployeeDailyReport::SHIFT_GENERAL,
                EmployeeDailyReport::SHIFT_FIRST,
                EmployeeDailyReport::SHIFT_SECOND,
                EmployeeDailyReport::SHIFT_NIGHT,
            ]),
            'first_half' => 'nullable|string|in:' . implode(',', [
                EmployeeDailyReport::HALF_PRESENT,
                EmployeeDailyReport::HALF_ABSENT,
                EmployeeDailyReport::HALF_LEAVE,
                EmployeeDailyReport::HALF_HOLIDAY,
            ]),
            'second_half' => 'nullable|string|in:' . implode(',', [
                EmployeeDailyReport::HALF_PRESENT,
                EmployeeDailyReport::HALF_ABSENT,
                EmployeeDailyReport::HALF_LEAVE,
                EmployeeDailyReport::HALF_HOLIDAY,
            ]),
            'in_spfid_1' => 'nullable|date_format:H:i:s',
            'out_spfid_1' => 'nullable|date_format:H:i:s',
            'in_spfid_2' => 'nullable|date_format:H:i:s',
            'out_spfid_2' => 'nullable|date_format:H:i:s',
            'late_in' => 'nullable|numeric|min:0',
            'early_out' => 'nullable|numeric|min:0',
            'hourly_paid_leave' => 'nullable|numeric|min:0',
            'hourly_unpaid_leave' => 'nullable|numeric|min:0',
            'over_time' => 'nullable|numeric|min:0',
            'auth_ot' => 'nullable|numeric|min:0',
            'auth_coff' => 'nullable|numeric|min:0',
            'work_hours' => 'nullable|numeric|min:0',
            'man_entry' => 'nullable|boolean',
            'reason' => 'nullable|string|max:500',
        ];
    }

    /**
     * Display the calendar view for attendance
     */
    public function calendarView(Request $request)
    {
        $employees = EmployeeDetails::orderBy('employee_id')->get();
        $employeeId = $request->input('employee_id');
        $month = $request->input('month', date('Y-m'));

        $attendanceMap = [];
        $attendanceDetails = [];
        $otHours = 0;
        $shortageHours = 0;
        $attendanceData = collect();
        $holidayMap = [];

        if ($employeeId) {
            $employee = $employees->where('employee_id', $employeeId)->first();

            if ($employee) {
                $start = Carbon::parse($month . '-01');
                $end = $start->copy()->endOfMonth();

                // Key attendance by date
                $attendance = EmployeeAttendance::where('employee_id', $employeeId)
                    ->whereBetween('date', [$start->toDateString(), $end->toDateString()])
                    ->get()
                    ->keyBy(fn($row) => Carbon::parse($row->date)->format('Y-m-d'));

                $attendanceData = $attendance->values();

                // Build attendance details for calendar
                foreach ($attendance as $date => $record) {
                    $attendanceDetails[$date] = [
                        'check_in' => $record->check_in ? Carbon::parse($record->check_in)->format('H:i') : null,
                        'check_out' => $record->check_out ? Carbon::parse($record->check_out)->format('H:i') : null,
                        'break_out' => $record->break_out ? Carbon::parse($record->break_out)->format('H:i') : null,
                        'break_in' => $record->break_in ? Carbon::parse($record->break_in)->format('H:i') : null,
                        'working_hours' => $record->working_hours,
                        'ot_hours' => $record->ot_hours,
                        'shortage_hours' => $record->shortage_hours,
                        'shift' => $record->shift,
                        'first_half' => $record->first_half,
                        'second_half' => $record->second_half,
                        'late_in' => $record->late_in,
                        'early_out' => $record->early_out,
                    ];
                }

                // Get holidays for the month
                $holidays = Holiday::whereBetween('date', [$start->toDateString(), $end->toDateString()])
                    ->get()
                    ->keyBy(fn($row) => Carbon::parse($row->date)->format('Y-m-d'));

                foreach ($holidays as $date => $holiday) {
                    $holidayMap[$date] = $holiday->name;
                }

                // Get leaves
                $leaves = EmployeeLeave::where('employee_id', $employee->id)
                    ->where('status', 'Approved')
                    ->where(function ($q) use ($start, $end) {
                        $q->whereBetween('leave_from', [$start, $end])
                            ->orWhereBetween('leave_to', [$start, $end])
                            ->orWhere(function ($q2) use ($start, $end) {
                                $q2->where('leave_from', '<', $start)
                                    ->where('leave_to', '>', $end);
                            });
                    })
                    ->get();

                $leaveDates = [];
                foreach ($leaves as $leave) {
                    $leaveFrom = Carbon::parse($leave->leave_from);
                    $leaveTo = Carbon::parse($leave->leave_to);
                    for ($ld = $leaveFrom->copy(); $ld->lte($leaveTo); $ld->addDay()) {
                        $leaveDates[$ld->format('Y-m-d')] = [
                            'type' => strtoupper($leave->leave_type),
                            'duration' => $leave->duration_type ?? 'Full Day',
                            'half_type' => $leave->half_type ?? null,
                        ];
                    }
                }

                // Build attendance map with priority
                for ($d = $start->copy(); $d->lte($end); $d->addDay()) {
                    $date = $d->toDateString();
                    $isSunday = $d->dayOfWeek === 0;
                    $isHoliday = isset($holidays[$date]);
                    $hasAttendance = isset($attendance[$date]) && $attendance[$date]->check_in && $attendance[$date]->check_out;
                    $hasLeave = isset($leaveDates[$date]);

                    $status = 'Absent';

                    if ($hasAttendance) {
                        $status = 'Present';
                    } elseif ($isHoliday) {
                        $status = 'Holiday';
                    } elseif ($hasLeave) {
                        $leaveType = $leaveDates[$date]['type'];
                        $status = $leaveType === 'LOP' ? 'LOP' : $leaveType;
                    } elseif ($isSunday) {
                        $status = 'Sunday';
                    }

                    $attendanceMap[$date] = $status;
                }

                // OT & Shortage Hours
                $otHours = round($attendanceData->sum('ot_hours'), 2);
                $shortageHours = round($attendanceData->sum('shortage_hours'), 2);
            }
        }

        return $this->roleView('employee.employeecalendar', compact(
            'employees',
            'attendanceMap',
            'attendanceDetails',
            'month',
            'employeeId',
            'otHours',
            'shortageHours',
            'attendanceData',
            'holidayMap'
        ));
    }

    /**
     * Get calendar data for an employee (AJAX)
     */
    public function calendar(int $employeeId, string $month): JsonResponse
    {
        $date = Carbon::parse($month . '-01');

        // Fetch daily report records
        $dailyRecords = DB::table('employee_daily_reports')
            ->where('employee_id', $employeeId)
            ->whereMonth('date', $date->month)
            ->whereYear('date', $date->year)
            ->get();

        $days = $dailyRecords->map(function ($item) {
            $status = 'Absent';
            if ($item->first_half === 'Present' || $item->second_half === 'Present') {
                $status = 'Present';
            } elseif ($item->hourly_paid_leave || $item->hourly_unpaid_leave) {
                $status = 'Leave';
            }

            return [
                'date' => $item->date,
                'status' => $status,
            ];
        });

        // Fetch summary data
        $summaryRecords = DB::table('employee_attendances')
            ->where('employee_id', $employeeId)
            ->whereMonth('month_year', $date->month)
            ->whereYear('month_year', $date->year)
            ->get();

        $summary = [
            'days_worked' => $summaryRecords->sum('days_worked'),
            'sundays_eligible' => $summaryRecords->sum('sundays_eligible'),
            'holiday_count' => $summaryRecords->sum('holidays'),
            'earned_leave' => 0,
            'enjoy_leave_count' => $summaryRecords->sum('leave_enjoyed'),
            'balance_leave_count' => 0,
            'total_paid_days' => $summaryRecords->sum('total_paid_days'),
            'absent_count' => $summaryRecords->sum('absent'),
            'first_shift_count' => $summaryRecords->sum('first_shifts'),
            'night_shift_count' => $summaryRecords->sum('night_shifts'),
            'ot_hours' => $summaryRecords->sum('ot_hours'),
            'shortage_hours' => $summaryRecords->sum('shortage_hours'),
        ];

        return response()->json([
            'days' => $days,
            'summary' => $summary,
        ]);
    }

    /**
     * Import daily reports from Excel
     */
    public function import(Request $request)
    {
        if (!$request->hasFile('import_file')) {
            return back()->with('error', 'No file uploaded.');
        }

        $file = $request->file('import_file');

        try {
            $spreadsheet = IOFactory::load($file->getPathname());
            $sheet = $spreadsheet->getActiveSheet();
            $rows = $sheet->toArray(null, true, true, true);

            $header = array_shift($rows); // first row as header

            foreach ($rows as $row) {

                EmployeeDailyReport::create([
                    'date' => $row['A'],
                    'employee_id' => $row['B'],
                    'shift' => $row['C'],
                    'in_spfid_1' => $row['D'],
                    'out_spfid_1' => $row['E'],
                    'in_spfid_2' => $row['F'],
                    'out_spfid_2' => $row['G'],
                    'first_half' => $row['H'],
                    'second_half' => $row['I'],
                    'late_in' => $row['J'],
                    'early_out' => $row['K'],
                    'hourly_paid_leave' => $row['L'],
                    'hourly_unpaid_leave' => $row['M'],
                    'over_time' => $row['N'],
                    'auth_ot' => $row['O'],
                    'auth_coff' => $row['P'],
                    'work_hours' => $row['Q'],
                    'man_entry' => $row['R'],
                    'reason' => $row['S'],
                ]);
                
            }

            return back()->with('success', 'Imported successfully');
        } catch (\Exception $e) {
            Log::error('Import error: ' . $e->getMessage());
            return back()->with('error', 'Failed to import file.');
        }
    }
}
