<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\AttendanceHoliday;
use App\Models\Branch;
use App\Models\SalaryPayment;
use App\Models\Staff;
use App\Models\StaffAttendance;
use App\Support\AdminAudit;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class StaffController extends Controller
{
    public function index()
    {
        $staffs = Staff::latest('id')->paginate(20);
        $branches = Branch::orderBy('name')->get();

        return view('admin.staff.index', compact('staffs', 'branches'));
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'branch_id' => 'nullable|exists:branches,id',
            'name' => 'required|string|max:120',
            'employee_code' => 'required|string|max:40|unique:staff,employee_code',
            'phone' => 'nullable|string|max:30',
            'email' => 'nullable|email|max:150',
            'designation' => 'nullable|string|max:80',
            'department' => 'nullable|string|max:80',
            'salary' => 'nullable|numeric|min:0',
            'commission_percent' => 'nullable|numeric|min:0',
            'incentive_amount' => 'nullable|numeric|min:0',
            'status' => 'required|in:Active,Inactive',
            'join_date' => 'nullable|date',
            'kpi_target_amount' => 'nullable|numeric|min:0',
            'notes' => 'nullable|string',
            'nfc_uid' => 'nullable|string|max:100|unique:staff,nfc_uid',
            'office_start_time' => 'nullable|date_format:H:i',
            'office_end_time' => 'nullable|date_format:H:i|after:office_start_time',
            'late_grace_minutes' => 'nullable|integer|min:0|max:180',
        ]);

        $data['office_start_time'] = $data['office_start_time'] ?? '09:00';
        $data['office_end_time'] = $data['office_end_time'] ?? '18:00';
        $data['late_grace_minutes'] = $data['late_grace_minutes'] ?? 15;

        $staff = Staff::create($data);
        AdminAudit::log('Staff', 'Create', $staff->employee_code, null, $staff->toArray());

        return back()->with('success', 'Staff created successfully.');
    }

    public function attendance(Request $request)
    {
        if ($request->isMethod('post')) {
            $action = $request->input('action_type', 'manual_update');

            if (in_array($action, ['nfc_check_in', 'nfc_check_out', 'auto_nfc_punch'], true)) {
                return $this->processNfcPunch($request, $action);
            }

            if ($action === 'save_holiday') {
                return $this->saveHoliday($request);
            }

            return $this->processManualAttendance($request);
        }

        $records = StaffAttendance::with('staff')
            ->latest('attendance_date')
            ->latest('updated_at')
            ->paginate(30);

        $staffs = Staff::orderBy('name')->get();
        $holidays = AttendanceHoliday::where('is_active', true)
            ->latest('holiday_date')
            ->take(20)
            ->get();

        $today = Carbon::today();
        $todayLog = StaffAttendance::with('staff')
            ->whereDate('attendance_date', $today->toDateString())
            ->latest('updated_at')
            ->get();

        return view('admin.staff.attendance', compact('records', 'staffs', 'holidays', 'todayLog'));
    }

    public function salary(Request $request)
    {
        if ($request->isMethod('post')) {
            $data = $request->validate([
                'staff_id' => 'required|exists:staff,id',
                'payment_month' => 'required|string|max:20',
                'gross_salary' => 'required|numeric|min:0',
                'commission_amount' => 'nullable|numeric|min:0',
                'incentive_amount' => 'nullable|numeric|min:0',
                'deduction_amount' => 'nullable|numeric|min:0',
                'net_salary' => 'required|numeric|min:0',
                'paid_date' => 'required|date',
                'payment_method' => 'required|string|max:40',
                'notes' => 'nullable|string',
            ]);
            SalaryPayment::create($data);

            return back()->with('success', 'Salary payment saved successfully.');
        }

        $payments = SalaryPayment::latest('id')->paginate(30);
        $staffs = Staff::orderBy('name')->get();

        return view('admin.staff.salary', compact('payments', 'staffs'));
    }

    protected function processNfcPunch(Request $request, string $action)
    {
        $data = $request->validate([
            'nfc_uid' => 'required|string|max:100',
            'notes' => 'nullable|string|max:500',
        ]);

        $staff = Staff::where('nfc_uid', $data['nfc_uid'])->first();

        if (! $staff) {
            return back()->withErrors(['nfc_uid' => 'No staff found for this NFC card/tag.'])->withInput();
        }

        if ($staff->status !== 'Active') {
            return back()->withErrors(['nfc_uid' => 'This staff is inactive. Attendance punch is blocked.'])->withInput();
        }

        $now = Carbon::now();
        $attendanceDate = $now->toDateString();
        $status = $this->resolveBaseStatus($staff, $attendanceDate);

        $attendance = StaffAttendance::firstOrNew([
            'staff_id' => $staff->id,
            'attendance_date' => $attendanceDate,
        ]);

        if ($status === 'Holiday' || $status === 'Weekend') {
            $attendance->status = $status;
        }

        $attendance->nfc_uid = $data['nfc_uid'];
        $attendance->notes = $this->combineNotes($attendance->notes, $data['notes'] ?? null);
        $attendance->last_punch_at = $now;
        $attendance->first_punch_at = $attendance->first_punch_at ?: $now;

        $resolvedAction = $action;
        if ($action === 'auto_nfc_punch') {
            $resolvedAction = $attendance->check_in && ! $attendance->check_out
                ? 'nfc_check_out'
                : 'nfc_check_in';
        }

        if ($resolvedAction === 'nfc_check_in') {
            if ($attendance->check_in && $attendance->check_out) {
                return back()->withErrors(['nfc_uid' => 'Today attendance is already completed for this staff.'])->withInput();
            }

            if ($attendance->check_in && ! $attendance->check_out) {
                $resolvedAction = 'nfc_check_out';
            } else {
                $attendance->check_in = $now;
            }
        }

        if ($resolvedAction === 'nfc_check_out') {
            if (! $attendance->check_in) {
                $attendance->check_in = $now;
                $resolvedAction = 'nfc_check_in';
            } elseif ($attendance->check_out) {
                return back()->withErrors(['nfc_uid' => 'Check-out already completed for today.'])->withInput();
            } else {
                $attendance->check_out = $now;
            }
        }

        $this->applyAttendanceMetrics($attendance, $staff, $attendanceDate);
        $attendance->save();

        AdminAudit::log(
            'Staff Attendance',
            $resolvedAction === 'nfc_check_in' ? 'NFC Check In' : 'NFC Check Out',
            $staff->employee_code,
            null,
            $attendance->toArray()
        );

        return back()->with('success', $staff->name . ' ' . ($resolvedAction === 'nfc_check_in' ? 'checked in' : 'checked out') . ' successfully.');
    }

    protected function processManualAttendance(Request $request)
    {
        $data = $request->validate([
            'staff_id' => 'required|exists:staff,id',
            'attendance_date' => 'required|date',
            'status' => ['nullable', Rule::in(['Present', 'Late', 'Absent', 'Half Day', 'Leave', 'Holiday', 'Weekend'])],
            'check_in' => 'nullable|date_format:H:i',
            'check_out' => 'nullable|date_format:H:i',
            'notes' => 'nullable|string',
            'manual_reason' => 'nullable|string|max:500',
            'is_missed_punch_adjusted' => 'nullable|boolean',
        ]);

        $staff = Staff::findOrFail($data['staff_id']);
        $attendanceDate = Carbon::parse($data['attendance_date'])->toDateString();

        $attendance = StaffAttendance::firstOrNew([
            'staff_id' => $staff->id,
            'attendance_date' => $attendanceDate,
        ]);

        $attendance->status = $data['status'] ?: $this->resolveBaseStatus($staff, $attendanceDate);
        $attendance->check_in = ! empty($data['check_in']) ? Carbon::parse($attendanceDate . ' ' . $data['check_in'] . ':00') : null;
        $attendance->check_out = ! empty($data['check_out']) ? Carbon::parse($attendanceDate . ' ' . $data['check_out'] . ':00') : null;
        $attendance->notes = $data['notes'] ?? null;
        $attendance->manual_reason = $data['manual_reason'] ?? 'Manual attendance correction';
        $attendance->is_manual = true;
        $attendance->is_missed_punch_adjusted = (bool) ($data['is_missed_punch_adjusted'] ?? false);
        $attendance->first_punch_at = $attendance->first_punch_at ?: now();
        $attendance->last_punch_at = now();

        $this->applyAttendanceMetrics($attendance, $staff, $attendanceDate);
        $attendance->save();

        AdminAudit::log('Staff Attendance', 'Manual Correction', $staff->employee_code, null, $attendance->toArray());

        return back()->with('success', 'Attendance saved successfully.');
    }

    protected function saveHoliday(Request $request)
    {
        $data = $request->validate([
            'holiday_date' => 'required|date|unique:attendance_holidays,holiday_date',
            'title' => 'required|string|max:120',
            'notes' => 'nullable|string|max:500',
        ]);

        AttendanceHoliday::create([
            'holiday_date' => $data['holiday_date'],
            'title' => $data['title'],
            'notes' => $data['notes'] ?? null,
            'is_active' => true,
        ]);

        return back()->with('success', 'Holiday saved successfully.');
    }

    protected function resolveBaseStatus(Staff $staff, string $attendanceDate): string
    {
        if ($this->isHoliday($attendanceDate)) {
            return 'Holiday';
        }

        if ($this->isWeekend($attendanceDate)) {
            return 'Weekend';
        }

        return 'Present';
    }

    protected function applyAttendanceMetrics(StaffAttendance $attendance, Staff $staff, string $attendanceDate): void
    {
        $startTime = $staff->office_start_time ?: '09:00:00';
        $endTime = $staff->office_end_time ?: '18:00:00';
        $grace = (int) ($staff->late_grace_minutes ?? 15);

        $shiftStart = Carbon::parse($attendanceDate . ' ' . substr($startTime, 0, 8));
        $shiftEnd = Carbon::parse($attendanceDate . ' ' . substr($endTime, 0, 8));

        $attendance->late_minutes = 0;
        $attendance->early_exit_minutes = 0;
        $attendance->overtime_minutes = 0;

        if ($attendance->check_in) {
            $allowedStart = (clone $shiftStart)->addMinutes($grace);
            if ($attendance->check_in->gt($allowedStart)) {
                $attendance->late_minutes = $attendance->check_in->diffInMinutes($shiftStart);
            }
        }

        if ($attendance->check_out) {
            if ($attendance->check_out->lt($shiftEnd)) {
                $attendance->early_exit_minutes = $attendance->check_out->diffInMinutes($shiftEnd);
            }

            if ($attendance->check_out->gt($shiftEnd)) {
                $attendance->overtime_minutes = $attendance->check_out->diffInMinutes($shiftEnd);
            }
        }

        if (in_array($attendance->status, ['Holiday', 'Weekend', 'Leave', 'Absent'], true)) {
            return;
        }

        if (! $attendance->check_in && ! $attendance->check_out) {
            $attendance->status = 'Absent';
            return;
        }

        if ($attendance->check_in && ! $attendance->check_out) {
            $attendance->status = $attendance->late_minutes > 0 ? 'Late' : 'Present';
            return;
        }

        if (! $attendance->check_in && $attendance->check_out) {
            $attendance->status = 'Half Day';
            return;
        }

        $workedMinutes = $attendance->check_in && $attendance->check_out
            ? $attendance->check_in->diffInMinutes($attendance->check_out, false)
            : 0;

        $scheduledMinutes = $shiftStart->diffInMinutes($shiftEnd, false);
        $halfDayThreshold = max((int) floor($scheduledMinutes / 2), 1);

        if ($workedMinutes > 0 && $workedMinutes < $halfDayThreshold) {
            $attendance->status = 'Half Day';
            return;
        }

        if ($attendance->late_minutes > 0) {
            $attendance->status = 'Late';
            return;
        }

        $attendance->status = 'Present';
    }

    protected function isWeekend(string $attendanceDate): bool
    {
        return Carbon::parse($attendanceDate)->isFriday();
    }

    protected function isHoliday(string $attendanceDate): bool
    {
        return AttendanceHoliday::whereDate('holiday_date', $attendanceDate)
            ->where('is_active', true)
            ->exists();
    }

    protected function combineNotes(?string $existing, ?string $new): ?string
    {
        $existing = trim((string) $existing);
        $new = trim((string) $new);

        if ($existing === '') {
            return $new !== '' ? $new : null;
        }

        if ($new === '') {
            return $existing;
        }

        return $existing . ' | ' . $new;
    }
}
