<?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');

        // Build employees query with filters
        $employeesQuery = EmployeeDetails::query();
        if ($department) {
            $employeesQuery->where('department', $department);
        }
        if ($employeeId) {
            $employeesQuery->where('id', $employeeId);
        }
        $employees = $employeesQuery->orderBy('employee_id')->get();

        // Get department list for filter dropdown
        $departments = EmployeeDetails::distinct()->pluck('department')->filter()->values();

        // Attendance for selected date
        $attendanceData = EmployeeAttendance::whereDate('date', $date)
            ->when($department, fn($q) => $q->where('department', $department))
            ->when($employeeId, fn($q) => $q->where('employee_id', $employees->first()?->employee_id))
            ->get();

        // Leave data for selected date
        $leaveData = EmployeeLeave::whereDate('leave_from', '<=', $date)
            ->whereDate('leave_to', '>=', $date)
            ->when($department, fn($q) => $q->where('department', $department))
            ->when($employeeId, function ($q) use ($employeeId) {
                $emp = EmployeeDetails::find($employeeId);
                if ($emp) {
                    $q->where('employee_id', $emp->id);
                }
            })
            ->get();

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

    /**
     * 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
    {
        if (!$this->isSuperAdmin() && !$this->isManager()) {
            return response()->json(['success' => false, 'message' => 'Unauthorized.'], 403);
        }

        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)
    {
        $request->validate([
            'import_file' => 'required|file|mimes:xlsx,xls|max:10240',
        ]);

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

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

            foreach ($rows as $index => $row) {
                if ($index === 0) continue; // Skip header
                if (count($row) < 19) continue;

                EmployeeDailyReport::create([
                    'date' => $row[0],
                    'employee_id' => $row[1],
                    'shift' => $row[2],
                    'in_spfid_1' => $row[3],
                    'out_spfid_1' => $row[4],
                    'in_spfid_2' => $row[5],
                    'out_spfid_2' => $row[6],
                    'first_half' => $row[7],
                    'second_half' => $row[8],
                    'late_in' => $row[9],
                    'early_out' => $row[10],
                    'hourly_paid_leave' => $row[11],
                    'hourly_unpaid_leave' => $row[12],
                    'over_time' => $row[13],
                    'auth_ot' => $row[14],
                    'auth_coff' => $row[15],
                    'work_hours' => $row[16],
                    'man_entry' => $row[17],
                    'reason' => $row[18],
                ]);
            }

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