<?php

namespace App\Http\Controllers\Epelcon;

use App\Http\Controllers\Controller;
use App\Models\Appraisal;
use App\Models\AttendanceRecord;
use App\Models\Department;
use App\Models\DutyRoster;
use App\Models\DutyShift;
use App\Models\Employee;
use App\Models\EmployeeLoan;
use App\Models\LeaveRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;

class HrController extends Controller
{
    public function dashboard()
    {
        $headcount = [
            'active' => Employee::where('status', 'active')->count(),
            'on_leave' => Employee::where('status', 'on_leave')->count(),
            'exited' => Employee::where('status', 'exited')->count(),
        ];

        $departmentsCount = Department::count();
        $presentToday = AttendanceRecord::whereDate('date', today())->whereIn('status', ['present', 'late', 'half_day'])->count();
        $absentToday = AttendanceRecord::whereDate('date', today())->where('status', 'absent')->count();
        $pendingLeave = LeaveRequest::where('status', 'pending')->count();

        $onDutyToday = DutyRoster::with(['employee', 'shift'])
            ->whereDate('date', today())
            ->get()
            ->sortBy(fn ($r) => $r->shift->starts_at ?? '')
            ->values();

        $upcomingLeaves = LeaveRequest::with('employee')
            ->where('status', 'approved')
            ->whereDate('end_date', '>=', today())
            ->orderBy('start_date')
            ->take(6)->get();

        $recentAppraisals = Appraisal::with('employee')->latest()->take(5)->get();

        $monthlyPayrollCost = (float) Employee::where('status', 'active')->sum('salary');

        return view('epelcon.hr.dashboard', compact(
            'headcount', 'departmentsCount', 'presentToday', 'absentToday',
            'pendingLeave', 'onDutyToday', 'upcomingLeaves', 'recentAppraisals', 'monthlyPayrollCost'
        ));
    }

    public function employees(Request $request)
    {
        $employees = Employee::with('department')
            ->when($request->filled('q'), function ($q) use ($request) {
                $q->where(fn ($w) => $w->where('name', 'like', "%{$request->q}%")
                    ->orWhere('employee_no', 'like', "%{$request->q}%"));
            })
            ->when($request->filled('department'), fn ($q) => $q->where('department_id', $request->department))
            ->orderBy('name')
            ->paginate(15)->withQueryString();

        $departments = Department::orderBy('name')->get();

        return view('epelcon.hr.employees', compact('employees', 'departments'));
    }

    public function storeEmployee(Request $request)
    {
        $data = $this->validateEmployee($request);
        $data['employee_no'] = $data['employee_no'] ?? 'EMP-' . strtoupper(Str::random(6));

        Employee::create($data);

        return back()->with('success', 'Employee added.');
    }

    public function updateEmployee(Request $request, Employee $employee)
    {
        $data = $this->validateEmployee($request);
        $data['employee_no'] = $data['employee_no'] ?? $employee->employee_no;

        $employee->update($data);

        return back()->with('success', 'Employee updated.');
    }

    protected function validateEmployee(Request $request): array
    {
        return $request->validate([
            'employee_no' => 'nullable|string|max:20',
            'name' => 'required|string|max:255',
            'email' => 'nullable|email|max:255',
            'phone' => 'nullable|string|max:40',
            'department_id' => 'nullable|exists:departments,id',
            'designation' => 'nullable|string|max:255',
            'employment_type' => 'required|in:full_time,part_time,contract,intern',
            'date_joined' => 'nullable|date',
            'salary' => 'required|numeric|min:0',
            'bank_name' => 'nullable|string|max:255',
            'account_number' => 'nullable|string|max:255',
            'status' => 'required|in:active,on_leave,exited',
            'address' => 'nullable|string|max:255',
        ]);
    }

    public function storeDepartment(Request $request)
    {
        $request->validate(['name' => 'required|string|max:255']);

        Department::create(['name' => $request->name]);

        return back()->with('success', 'Department added.');
    }

    public function attendance(Request $request)
    {
        $date = $request->filled('date') ? Carbon::parse($request->date) : today();

        $employees = Employee::with('department')->where('status', '!=', 'exited')->orderBy('name')->get();

        $records = AttendanceRecord::whereDate('date', $date)->get()->keyBy('employee_id');

        $summary = [
            'present' => $records->whereIn('status', ['present', 'late', 'half_day'])->count(),
            'absent' => $records->where('status', 'absent')->count(),
            'on_leave' => $records->where('status', 'leave')->count(),
            'unmarked' => $employees->count() - $records->count(),
        ];

        return view('epelcon.hr.attendance', compact('date', 'employees', 'records', 'summary'));
    }

    public function markAttendance(Request $request)
    {
        $request->validate([
            'date' => 'required|date',
            'rows' => 'required|array',
            'rows.*.status' => 'required|in:present,absent,late,half_day,leave',
            'rows.*.clock_in' => 'nullable',
            'rows.*.clock_out' => 'nullable',
        ]);

        $date = Carbon::parse($request->date)->toDateString();
        $count = 0;

        foreach ($request->rows as $employeeId => $row) {
            if (! Employee::whereKey($employeeId)->exists()) {
                continue;
            }

            AttendanceRecord::updateOrCreate(
                ['employee_id' => $employeeId, 'date' => $date],
                [
                    'status' => $row['status'],
                    'clock_in' => $row['clock_in'] ?: null,
                    'clock_out' => $row['clock_out'] ?: null,
                ]
            );
            $count++;
        }

        return back()->with('success', "Attendance saved for {$count} employees.");
    }

    public function leave(Request $request)
    {
        $leaves = LeaveRequest::with(['employee.department', 'approver'])
            ->when($request->filled('status'), fn ($q) => $q->where('status', $request->status))
            ->latest()
            ->paginate(15)->withQueryString();

        $counts = [
            'pending' => LeaveRequest::where('status', 'pending')->count(),
            'approved' => LeaveRequest::where('status', 'approved')->count(),
            'rejected' => LeaveRequest::where('status', 'rejected')->count(),
        ];

        $employees = Employee::where('status', '!=', 'exited')->orderBy('name')->get(['id', 'name']);

        return view('epelcon.hr.leave', compact('leaves', 'counts', 'employees'));
    }

    public function storeLeave(Request $request)
    {
        $data = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'type' => 'required|in:annual,sick,casual,maternity,unpaid',
            'start_date' => 'required|date',
            'end_date' => 'required|date|after_or_equal:start_date',
            'reason' => 'nullable|string',
        ]);

        $data['days'] = Carbon::parse($data['start_date'])->diffInDays(Carbon::parse($data['end_date'])) + 1;
        $data['status'] = 'pending';

        LeaveRequest::create($data);

        return back()->with('success', 'Leave request submitted.');
    }

    public function setLeaveStatus(Request $request, LeaveRequest $leave)
    {
        $request->validate(['status' => 'required|in:approved,rejected,pending']);

        $leave->update([
            'status' => $request->status,
            'approved_by' => $request->status === 'approved' ? auth()->id() : $leave->approved_by,
        ]);

        if ($request->status === 'approved'
            && $leave->start_date->lte(today())
            && $leave->end_date->gte(today())
            && $leave->employee) {
            $leave->employee->update(['status' => 'on_leave']);
        }

        return back()->with('success', 'Leave request ' . $request->status . '.');
    }

    public function roster(Request $request)
    {
        $weekStart = ($request->filled('week') ? Carbon::parse($request->week) : today())->startOfWeek(Carbon::MONDAY);
        $days = collect(range(0, 6))->map(fn ($i) => $weekStart->copy()->addDays($i));

        $employees = Employee::with('department')->where('status', '!=', 'exited')->orderBy('name')->get();
        $shifts = DutyShift::orderBy('starts_at')->get();

        $rosters = DutyRoster::with('shift')
            ->whereBetween('date', [$weekStart->toDateString(), $weekStart->copy()->addDays(6)->toDateString()])
            ->get()
            ->groupBy(fn ($r) => $r->employee_id . '|' . $r->date->toDateString());

        return view('epelcon.hr.roster', compact('weekStart', 'days', 'employees', 'shifts', 'rosters'));
    }

    public function storeRoster(Request $request)
    {
        $data = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'duty_shift_id' => 'required|exists:duty_shifts,id',
            'date' => 'required|date',
            'notes' => 'nullable|string',
        ]);

        DutyRoster::updateOrCreate(
            ['employee_id' => $data['employee_id'], 'date' => $data['date']],
            ['duty_shift_id' => $data['duty_shift_id'], 'notes' => $data['notes'] ?? null]
        );

        return back()->with('success', 'Shift assigned.');
    }

    public function storeShift(Request $request)
    {
        $data = $request->validate([
            'name' => 'required|string|max:255',
            'starts_at' => 'required',
            'ends_at' => 'required',
        ]);

        DutyShift::create($data);

        return back()->with('success', 'Shift added.');
    }

    public function loans(Request $request)
    {
        $loans = EmployeeLoan::with('employee')
            ->when($request->filled('status'), fn ($q) => $q->where('status', $request->status))
            ->latest('issued_on')
            ->paginate(15)->withQueryString();

        $summary = [
            'active' => EmployeeLoan::where('status', 'active')->count(),
            'outstanding' => (float) EmployeeLoan::where('status', 'active')->sum('balance'),
        ];

        $employees = Employee::where('status', '!=', 'exited')->orderBy('name')->get(['id', 'name']);

        return view('epelcon.hr.loans', compact('loans', 'summary', 'employees'));
    }

    public function storeLoan(Request $request)
    {
        $data = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'amount' => 'required|numeric|min:0.01',
            'monthly_deduction' => 'required|numeric|min:0.01',
            'reason' => 'nullable|string',
            'issued_on' => 'required|date',
        ]);

        EmployeeLoan::create($data + ['balance' => $data['amount'], 'status' => 'active']);

        return back()->with('success', 'Loan issued.');
    }

    public function setLoanStatus(Request $request, EmployeeLoan $loan)
    {
        $request->validate(['status' => 'required|in:active,settled']);

        $loan->update([
            'status' => $request->status,
            'balance' => $request->status === 'settled' ? 0 : $loan->balance,
        ]);

        return back()->with('success', 'Loan updated.');
    }

    public function appraisals(Request $request)
    {
        $appraisals = Appraisal::with(['employee.department', 'appraiser'])
            ->when($request->filled('q'), function ($q) use ($request) {
                $q->whereHas('employee', fn ($w) => $w->where('name', 'like', "%{$request->q}%"));
            })
            ->latest()
            ->paginate(15)->withQueryString();

        $summary = [
            'total' => Appraisal::count(),
            'average' => round((float) Appraisal::avg('score'), 1),
        ];

        $employees = Employee::where('status', '!=', 'exited')->orderBy('name')->get(['id', 'name']);

        return view('epelcon.hr.appraisals', compact('appraisals', 'summary', 'employees'));
    }

    public function storeAppraisal(Request $request)
    {
        $data = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'period' => 'required|string|max:20',
            'score' => 'required|integer|min:1|max:10',
            'strengths' => 'nullable|string',
            'improvements' => 'nullable|string',
        ]);

        Appraisal::create($data + ['appraised_by' => auth()->id()]);

        return back()->with('success', 'Appraisal recorded.');
    }
}
