<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Announcement;
use App\Models\AttendanceRecord;
use App\Models\Department;
use App\Models\Designation;
use App\Models\DisciplinaryRecord;
use App\Models\EmployeeDocument;
use App\Models\HrTicket;
use App\Models\JobApplication;
use App\Models\JobPosting;
use App\Models\LeaveRequest;
use App\Models\LeaveType;
use App\Models\Payroll;
use App\Models\PerformanceReview;
use App\Models\Training;
use App\Models\TrainingEnrollment;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class HrAdminController extends Controller
{
    public function dashboard()
    {
        return view('admin.dashboard', [
            'totalEmployees' => User::count(),
            'activeEmployees' => User::where('employment_status', 'active')->count(),
            'pendingLeaves' => LeaveRequest::where('status', 'pending')->count(),
            'openTickets' => HrTicket::whereIn('status', ['open', 'in_progress'])->count(),
            'monthlyPayroll' => Payroll::whereMonth('pay_period_end', now()->month)->whereYear('pay_period_end', now()->year)->sum('net_pay'),
            'recentLeaves' => LeaveRequest::with(['user', 'leaveType'])->latest()->limit(6)->get(),
            'recentTickets' => HrTicket::with('user')->latest()->limit(6)->get(),
            'todayAttendance' => AttendanceRecord::whereDate('work_date', today())->count(),
        ]);
    }

    public function employees(Request $request)
    {
        $employees = User::with(['department', 'designation'])
            ->when($request->search, fn ($q, $search) => $q->where(fn ($inner) => $inner
                ->where('name', 'like', "%{$search}%")
                ->orWhere('email', 'like', "%{$search}%")
                ->orWhere('staff_id', 'like', "%{$search}%")))
            ->when($request->employment_status, fn ($q, $status) => $q->where('employment_status', $status))
            ->latest()
            ->paginate(15)
            ->withQueryString();

        return view('admin.employees', [
            'employees' => $employees,
            'departments' => Department::where('status', 'active')->orderBy('name')->get(),
            'designations' => Designation::where('status', 'active')->orderBy('name')->get(),
        ]);
    }

    public function storeEmployee(Request $request)
    {
        $data = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', 'unique:users,email'],
            'phone' => ['nullable', 'string', 'max:50'],
            'gender' => ['nullable', 'string', 'max:30'],
            'date_of_birth' => ['nullable', 'date'],
            'address' => ['nullable', 'string', 'max:255'],
            'emergency_contact' => ['nullable', 'string', 'max:255'],
            'department_id' => ['nullable', 'exists:departments,id'],
            'designation_id' => ['nullable', 'exists:designations,id'],
            'job_title' => ['nullable', 'string', 'max:255'],
            'employment_date' => ['nullable', 'date'],
            'employment_status' => ['required', 'string', 'max:50'],
            'basic_salary' => ['nullable', 'numeric', 'min:0'],
            'bank_name' => ['nullable', 'string', 'max:255'],
            'bank_account' => ['nullable', 'string', 'max:100'],
            'password' => ['required', 'min:8'],
        ]);

        $data['staff_id'] = $data['staff_id'] ?? 'HR-' . now()->format('Ymd') . '-' . Str::upper(Str::random(4));
        $data['role'] = 'user';
        User::create($data);

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

    public function showEmployee(User $employee)
    {
        $employee->load(['department', 'designation', 'documents', 'leaveRequests.leaveType', 'attendanceRecords', 'payrolls', 'performanceReviews', 'tickets']);

        return view('admin.employee-show', [
            'employee' => $employee,
            'departments' => Department::where('status', 'active')->orderBy('name')->get(),
            'designations' => Designation::where('status', 'active')->orderBy('name')->get(),
        ]);
    }

    public function updateEmployee(Request $request, User $employee)
    {
        $data = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', 'unique:users,email,' . $employee->id],
            'phone' => ['nullable', 'string', 'max:50'],
            'gender' => ['nullable', 'string', 'max:30'],
            'date_of_birth' => ['nullable', 'date'],
            'address' => ['nullable', 'string', 'max:255'],
            'emergency_contact' => ['nullable', 'string', 'max:255'],
            'department_id' => ['nullable', 'exists:departments,id'],
            'designation_id' => ['nullable', 'exists:designations,id'],
            'job_title' => ['nullable', 'string', 'max:255'],
            'employment_date' => ['nullable', 'date'],
            'employment_status' => ['required', 'string', 'max:50'],
            'basic_salary' => ['nullable', 'numeric', 'min:0'],
            'bank_name' => ['nullable', 'string', 'max:255'],
            'bank_account' => ['nullable', 'string', 'max:100'],
            'password' => ['nullable', 'min:8'],
        ]);

        if (! filled($data['password'] ?? null)) {
            unset($data['password']);
        }

        $employee->update($data);

        return back()->with('success', 'Employee updated successfully. Role change is intentionally left for direct database update only.');
    }

    public function departments()
    {
        return view('admin.departments', [
            'departments' => Department::with(['manager'])->withCount('employees')->orderBy('name')->get(),
            'employees' => User::orderBy('name')->get(),
        ]);
    }

    public function storeDepartment(Request $request)
    {
        Department::create($request->validate([
            'name' => ['required', 'string', 'max:255', 'unique:departments,name'],
            'code' => ['nullable', 'string', 'max:50', 'unique:departments,code'],
            'description' => ['nullable', 'string'],
            'manager_user_id' => ['nullable', 'exists:users,id'],
            'status' => ['required', 'string'],
        ]));

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

    public function updateDepartment(Request $request, Department $department)
    {
        $department->update($request->validate([
            'name' => ['required', 'string', 'max:255', 'unique:departments,name,' . $department->id],
            'code' => ['nullable', 'string', 'max:50', 'unique:departments,code,' . $department->id],
            'description' => ['nullable', 'string'],
            'manager_user_id' => ['nullable', 'exists:users,id'],
            'status' => ['required', 'string'],
        ]));

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

    public function designations()
    {
        return view('admin.designations', [
            'designations' => Designation::withCount('employees')->orderBy('name')->get(),
        ]);
    }

    public function storeDesignation(Request $request)
    {
        Designation::create($request->validate([
            'name' => ['required', 'string', 'max:255', 'unique:designations,name'],
            'level' => ['nullable', 'string', 'max:100'],
            'description' => ['nullable', 'string'],
            'status' => ['required', 'string'],
        ]));

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

    public function updateDesignation(Request $request, Designation $designation)
    {
        $designation->update($request->validate([
            'name' => ['required', 'string', 'max:255', 'unique:designations,name,' . $designation->id],
            'level' => ['nullable', 'string', 'max:100'],
            'description' => ['nullable', 'string'],
            'status' => ['required', 'string'],
        ]));

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

    public function attendance(Request $request)
    {
        return view('admin.attendance', [
            'records' => AttendanceRecord::with('user')
                ->when($request->user_id, fn ($q, $id) => $q->where('user_id', $id))
                ->when($request->work_date, fn ($q, $date) => $q->whereDate('work_date', $date))
                ->latest('work_date')
                ->paginate(20)
                ->withQueryString(),
            'employees' => User::orderBy('name')->get(),
        ]);
    }

    public function storeAttendance(Request $request)
    {
        $data = $request->validate([
            'user_id' => ['required', 'exists:users,id'],
            'work_date' => ['required', 'date'],
            'clock_in_at' => ['nullable', 'date'],
            'clock_out_at' => ['nullable', 'date', 'after_or_equal:clock_in_at'],
            'status' => ['required', 'string'],
            'notes' => ['nullable', 'string'],
        ]);
        $data['approved_by'] = auth()->id();
        AttendanceRecord::updateOrCreate(['user_id' => $data['user_id'], 'work_date' => $data['work_date']], $data);

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

    public function leaves(Request $request)
    {
        return view('admin.leaves', [
            'leaves' => LeaveRequest::with(['user', 'leaveType'])
                ->when($request->status, fn ($q, $status) => $q->where('status', $status))
                ->latest()
                ->paginate(20)
                ->withQueryString(),
            'leaveTypes' => LeaveType::orderBy('name')->get(),
        ]);
    }

    public function storeLeaveType(Request $request)
    {
        LeaveType::create($request->validate([
            'name' => ['required', 'string', 'max:255', 'unique:leave_types,name'],
            'annual_days' => ['required', 'integer', 'min:0'],
            'requires_document' => ['nullable', 'boolean'],
            'status' => ['required', 'string'],
        ]) + ['requires_document' => $request->boolean('requires_document')]);

        return back()->with('success', 'Leave type saved.');
    }

    public function reviewLeave(Request $request, LeaveRequest $leave)
    {
        $data = $request->validate([
            'status' => ['required', 'in:approved,rejected,pending'],
            'admin_comment' => ['nullable', 'string'],
        ]);
        $data['reviewed_by'] = auth()->id();
        $data['reviewed_at'] = now();
        $leave->update($data);

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

    public function payrolls(Request $request)
    {
        return view('admin.payrolls', [
            'payrolls' => Payroll::with('user')
                ->when($request->user_id, fn ($q, $id) => $q->where('user_id', $id))
                ->latest('pay_period_end')
                ->paginate(20)
                ->withQueryString(),
            'employees' => User::orderBy('name')->get(),
        ]);
    }

    public function storePayroll(Request $request)
    {
        $data = $request->validate([
            'user_id' => ['required', 'exists:users,id'],
            'pay_period_start' => ['required', 'date'],
            'pay_period_end' => ['required', 'date', 'after_or_equal:pay_period_start'],
            'basic_salary' => ['required', 'numeric', 'min:0'],
            'allowances' => ['nullable', 'numeric', 'min:0'],
            'deductions' => ['nullable', 'numeric', 'min:0'],
            'tax' => ['nullable', 'numeric', 'min:0'],
            'status' => ['required', 'string'],
            'paid_at' => ['nullable', 'date'],
            'notes' => ['nullable', 'string'],
        ]);
        $data = $this->calculatePayroll($data);
        Payroll::create($data);

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

    public function updatePayroll(Request $request, Payroll $payroll)
    {
        $data = $request->validate([
            'basic_salary' => ['required', 'numeric', 'min:0'],
            'allowances' => ['nullable', 'numeric', 'min:0'],
            'deductions' => ['nullable', 'numeric', 'min:0'],
            'tax' => ['nullable', 'numeric', 'min:0'],
            'status' => ['required', 'string'],
            'paid_at' => ['nullable', 'date'],
            'notes' => ['nullable', 'string'],
        ]);
        $payroll->update($this->calculatePayroll($data));

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

    private function calculatePayroll(array $data): array
    {
        $data['allowances'] = $data['allowances'] ?? 0;
        $data['deductions'] = $data['deductions'] ?? 0;
        $data['tax'] = $data['tax'] ?? 0;
        $data['net_pay'] = ($data['basic_salary'] + $data['allowances']) - ($data['deductions'] + $data['tax']);
        return $data;
    }

    public function documents()
    {
        return view('admin.documents', [
            'documents' => EmployeeDocument::with('user')->latest()->paginate(20),
            'employees' => User::orderBy('name')->get(),
        ]);
    }

    public function storeDocument(Request $request)
    {
        $data = $request->validate([
            'user_id' => ['required', 'exists:users,id'],
            'title' => ['required', 'string', 'max:255'],
            'document_type' => ['nullable', 'string', 'max:100'],
            'file' => ['nullable', 'file', 'max:5120'],
            'expiry_date' => ['nullable', 'date'],
            'status' => ['required', 'string'],
            'notes' => ['nullable', 'string'],
        ]);
        if ($request->hasFile('file')) {
            $data['file_path'] = $request->file('file')->store('hr-documents', 'public');
        }
        unset($data['file']);
        EmployeeDocument::create($data);

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

    public function performance()
    {
        return view('admin.performance', [
            'reviews' => PerformanceReview::with(['user', 'reviewer'])->latest()->paginate(20),
            'employees' => User::orderBy('name')->get(),
        ]);
    }

    public function storePerformance(Request $request)
    {
        $data = $request->validate([
            'user_id' => ['required', 'exists:users,id'],
            'review_period' => ['required', 'string', 'max:100'],
            'score' => ['required', 'integer', 'min:1', 'max:5'],
            'summary' => ['nullable', 'string'],
            'strengths' => ['nullable', 'string'],
            'improvement_plan' => ['nullable', 'string'],
            'status' => ['required', 'string'],
        ]);
        $data['reviewed_by'] = auth()->id();
        $data['reviewed_at'] = now();
        PerformanceReview::create($data);

        return back()->with('success', 'Performance review saved.');
    }

    public function announcements()
    {
        return view('admin.announcements', [
            'announcements' => Announcement::latest()->paginate(20),
        ]);
    }

    public function storeAnnouncement(Request $request)
    {
        Announcement::create($request->validate([
            'title' => ['required', 'string', 'max:255'],
            'body' => ['required', 'string'],
            'audience' => ['required', 'string'],
            'status' => ['required', 'string'],
            'published_at' => ['nullable', 'date'],
        ]) + ['created_by' => auth()->id(), 'published_at' => $request->published_at ?: now()]);

        return back()->with('success', 'Announcement published.');
    }

    public function jobs()
    {
        return view('admin.jobs', [
            'jobs' => JobPosting::with(['department'])->withCount('applications')->latest()->paginate(15),
            'departments' => Department::where('status', 'active')->orderBy('name')->get(),
        ]);
    }

    public function storeJob(Request $request)
    {
        JobPosting::create($request->validate([
            'title' => ['required', 'string', 'max:255'],
            'department_id' => ['nullable', 'exists:departments,id'],
            'employment_type' => ['required', 'string'],
            'location' => ['nullable', 'string', 'max:255'],
            'description' => ['nullable', 'string'],
            'requirements' => ['nullable', 'string'],
            'status' => ['required', 'string'],
            'closing_date' => ['nullable', 'date'],
        ]) + ['created_by' => auth()->id()]);

        return back()->with('success', 'Job posting saved.');
    }

    public function applications()
    {
        return view('admin.applications', [
            'applications' => JobApplication::with('jobPosting')->latest()->paginate(20),
        ]);
    }

    public function reviewApplication(Request $request, JobApplication $application)
    {
        $application->update($request->validate([
            'status' => ['required', 'string'],
            'notes' => ['nullable', 'string'],
        ]));

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

    public function trainings()
    {
        return view('admin.trainings', [
            'trainings' => Training::withCount('enrollments')->latest()->paginate(20),
            'employees' => User::orderBy('name')->get(),
        ]);
    }

    public function storeTraining(Request $request)
    {
        Training::create($request->validate([
            'title' => ['required', 'string', 'max:255'],
            'description' => ['nullable', 'string'],
            'start_date' => ['nullable', 'date'],
            'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
            'location' => ['nullable', 'string', 'max:255'],
            'status' => ['required', 'string'],
        ]) + ['created_by' => auth()->id()]);

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

    public function enrollTraining(Request $request, Training $training)
    {
        $data = $request->validate([
            'user_id' => ['required', 'exists:users,id'],
            'status' => ['required', 'string'],
            'notes' => ['nullable', 'string'],
        ]);
        TrainingEnrollment::updateOrCreate(['training_id' => $training->id, 'user_id' => $data['user_id']], $data + ['training_id' => $training->id]);

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

    public function tickets()
    {
        return view('admin.tickets', [
            'tickets' => HrTicket::with(['user', 'assignee'])->latest()->paginate(20),
            'admins' => User::where('role', 'admin')->orderBy('name')->get(),
        ]);
    }

    public function updateTicket(Request $request, HrTicket $ticket)
    {
        $data = $request->validate([
            'status' => ['required', 'string'],
            'priority' => ['required', 'string'],
            'response' => ['nullable', 'string'],
            'assigned_to' => ['nullable', 'exists:users,id'],
        ]);
        if ($data['status'] === 'resolved') {
            $data['resolved_at'] = now();
        }
        $ticket->update($data);

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

    public function discipline()
    {
        return view('admin.discipline', [
            'records' => DisciplinaryRecord::with(['user', 'issuer'])->latest()->paginate(20),
            'employees' => User::orderBy('name')->get(),
        ]);
    }

    public function storeDiscipline(Request $request)
    {
        DisciplinaryRecord::create($request->validate([
            'user_id' => ['required', 'exists:users,id'],
            'case_title' => ['required', 'string', 'max:255'],
            'incident_date' => ['nullable', 'date'],
            'description' => ['nullable', 'string'],
            'action_taken' => ['nullable', 'string'],
            'status' => ['required', 'string'],
        ]) + ['issued_by' => auth()->id()]);

        return back()->with('success', 'Disciplinary record saved.');
    }
}
