<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Attendance;
use App\Models\AttendanceRegularization;
use App\Models\User;
use App\Models\WorkDuration;
use App\Notifications\AttendanceRegularizationRequested;
use App\Notifications\AttendanceRegularizationReviewed;
use App\Services\HrmsNotificationService;
use Carbon\Carbon;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;

class AttendanceRegularizationController extends Controller
{
    public function index(Request $request)
    {
        $user = Auth::user();
        $companyId = $user->company_id;

        $year = $request->filled('year') ? (int) $request->year : (int) date('Y');
        $month = $request->filled('month') ? (int) $request->month : (int) date('n');
        $year = max(2020, min(2030, $year));
        $month = max(1, min(12, $month));

        $startOfMonth = Carbon::createFromDate($year, $month, 1)->startOfDay();
        $endOfMonth = $startOfMonth->copy()->endOfMonth();

        $query = AttendanceRegularization::with(['user', 'reviewer', 'attendance'])
            ->where('company_id', $companyId)
            ->whereBetween('date', [$startOfMonth->toDateString(), $endOfMonth->toDateString()])
            ->orderByDesc('date');

        $canReviewCompanyWide = (bool) $user?->canReviewAttendanceRegularizationsCompanyWide();

        $directReportIds = $user?->visibleStaffUserIds() ?? [];

        $isManager = count($directReportIds) > 0;
        if (! $canReviewCompanyWide) {
            if ($isManager) {
                $query->where(function ($q) use ($user, $directReportIds) {
                    $q->where('user_id', $user->id)->orWhereIn('user_id', $directReportIds);
                });
            } else {
                $query->where('user_id', $user->id);
            }
        }

        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }

        $regularizations = $query->paginate(15)->withQueryString();

        $canApproveIds = [];
        foreach ($regularizations->items() as $regularization) {
            if ($user?->canApproveRegularization($regularization)) {
                $canApproveIds[] = $regularization->id;
            }
        }

        $pendingRemoteQuery = Attendance::with('user')
            ->where('company_id', $companyId)
            ->where('attendance_type', 'remote')
            ->where('remote_approval_status', 'pending')
            ->whereBetween('date', [$startOfMonth->toDateString(), $endOfMonth->toDateString()])
            ->orderByDesc('date');
        if (! $canReviewCompanyWide) {
            if ($isManager) {
                $pendingRemoteQuery->where(function ($q) use ($user, $directReportIds) {
                    $q->where('user_id', $user->id)->orWhereIn('user_id', $directReportIds);
                });
            } else {
                $pendingRemoteQuery->where('user_id', $user->id);
            }
        }
        $pendingRemoteAttendances = $pendingRemoteQuery->get();
        $canApproveRemoteIds = [];
        foreach ($pendingRemoteAttendances as $remoteAttendance) {
            if ($user?->canReviewRemoteAttendance($remoteAttendance)) {
                $canApproveRemoteIds[] = $remoteAttendance->id;
            }
        }

        return view('attendance-regularizations.index', compact(
            'regularizations',
            'year',
            'month',
            'canApproveIds',
            'isManager',
            'canReviewCompanyWide',
            'pendingRemoteAttendances',
            'canApproveRemoteIds'
        ));
    }

    public function create(Request $request)
    {
        $prefillDate = $request->filled('date')
            ? Carbon::parse($request->date)->toDateString()
            : null;

        return view('attendance-regularizations.create', [
            'prefillDate' => $prefillDate,
        ]);
    }

    public function store(Request $request)
    {
        $request->merge([
            'requested_clock_in_time' => $this->normalizeTimeInput($request->input('requested_clock_in_time')),
            'requested_clock_out_time' => $this->normalizeTimeInput($request->input('requested_clock_out_time')),
        ]);

        $validated = $request->validate([
            'date' => 'required|date|before_or_equal:today',
            'requested_clock_in_time' => 'nullable|date_format:H:i',
            'requested_clock_out_time' => 'nullable|date_format:H:i',
            'reason' => 'required|string|max:1000',
            'redirect_to' => 'nullable|in:attendance',
            'year' => 'nullable|integer',
            'month' => 'nullable|integer|min:1|max:12',
            'employee_id' => 'nullable|integer',
        ]);

        if (
            empty($validated['requested_clock_in_time']) &&
            empty($validated['requested_clock_out_time'])
        ) {
            throw ValidationException::withMessages([
                'requested_clock_in_time' => 'Enter at least a requested clock-in or clock-out time.',
            ]);
        }

        if (
            ! empty($validated['requested_clock_in_time']) &&
            ! empty($validated['requested_clock_out_time']) &&
            $validated['requested_clock_out_time'] < $validated['requested_clock_in_time']
        ) {
            throw ValidationException::withMessages([
                'requested_clock_out_time' => 'Requested clock-out time must be after or equal to clock-in time.',
            ]);
        }

        $user = Auth::user();
        $date = Carbon::parse($validated['date'])->toDateString();

        $pendingExists = AttendanceRegularization::query()
            ->where('company_id', $user->company_id)
            ->where('user_id', $user->id)
            ->whereDate('date', $date)
            ->where('status', AttendanceRegularization::STATUS_PENDING)
            ->exists();
        if ($pendingExists) {
            throw ValidationException::withMessages([
                'date' => 'You already have a pending regularization request for this date.',
            ]);
        }

        $requestedClockIn = ! empty($validated['requested_clock_in_time'])
            ? Carbon::parse($date.' '.$validated['requested_clock_in_time'])
            : null;
        $requestedClockOut = ! empty($validated['requested_clock_out_time'])
            ? Carbon::parse($date.' '.$validated['requested_clock_out_time'])
            : null;

        $attendance = Attendance::where('company_id', $user->company_id)
            ->where('user_id', $user->id)
            ->where('date', $date)
            ->first();

        $regularization = AttendanceRegularization::create([
            'company_id' => $user->company_id,
            'user_id' => $user->id,
            'attendance_id' => $attendance?->id,
            'date' => $date,
            'requested_clock_in' => $requestedClockIn,
            'requested_clock_out' => $requestedClockOut,
            'reason' => $validated['reason'],
            'status' => AttendanceRegularization::STATUS_PENDING,
        ]);

        app(HrmsNotificationService::class)->notifyRegularizationApprovers(
            $user,
            new AttendanceRegularizationRequested($regularization)
        );

        if (($validated['redirect_to'] ?? null) === 'attendance') {
            return redirect()->route('attendance.index', array_filter([
                'year' => $validated['year'] ?? Carbon::parse($date)->year,
                'month' => $validated['month'] ?? Carbon::parse($date)->month,
                'employee_id' => $validated['employee_id'] ?? $user->id,
            ]))->with('success', 'Regularization request submitted. Your manager will be notified.');
        }

        return redirect()
            ->route('attendance-regularizations.index', $this->indexRedirectParams($request))
            ->with('success', 'Regularization request submitted. Your manager will be notified.');
    }

    public function approve(Request $request, AttendanceRegularization $attendanceRegularization)
    {
        if ($attendanceRegularization->status !== AttendanceRegularization::STATUS_PENDING) {
            return $this->redirectAfterReview($request, 'Request already processed.', 'error');
        }

        $attendanceRegularization->load('user');
        $reviewer = Auth::user();
        if (! $reviewer?->canApproveRegularization($attendanceRegularization)) {
            abort(403, 'You are not allowed to approve this request.');
        }

        $request->validate(['review_remarks' => 'nullable|string|max:500']);
        $this->applyApproval($attendanceRegularization, $reviewer, $request->input('review_remarks'));

        return $this->redirectAfterReview($request, 'Request approved.', 'success');
    }

    public function reject(Request $request, AttendanceRegularization $attendanceRegularization)
    {
        if ($attendanceRegularization->status !== AttendanceRegularization::STATUS_PENDING) {
            return $this->redirectAfterReview($request, 'Request already processed.', 'error');
        }

        $attendanceRegularization->load('user');
        $reviewer = Auth::user();
        if (! $reviewer?->canApproveRegularization($attendanceRegularization)) {
            abort(403, 'You are not allowed to reject this request.');
        }

        $request->validate(['review_remarks' => 'nullable|string|max:500']);
        $this->applyRejection($attendanceRegularization, $reviewer, $request->input('review_remarks'));

        return $this->redirectAfterReview($request, 'Request rejected.', 'success');
    }

    public function bulkReview(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'action' => ['required', Rule::in(['approve', 'reject'])],
            'ids' => ['required', 'array', 'min:1'],
            'ids.*' => ['integer'],
            'review_remarks' => 'nullable|string|max:500',
            'year' => 'nullable|integer',
            'month' => 'nullable|integer|min:1|max:12',
            'status' => 'nullable|string|in:pending,approved,rejected',
        ]);

        $reviewer = Auth::user();
        $companyId = (int) ($reviewer?->company_id ?? 0);
        $ids = array_values(array_unique(array_map('intval', $validated['ids'])));
        $remarks = $validated['review_remarks'] ?? null;
        $action = $validated['action'];

        $processed = 0;
        $skipped = 0;

        DB::transaction(function () use ($ids, $companyId, $reviewer, $remarks, $action, &$processed, &$skipped) {
            $regularizations = AttendanceRegularization::query()
                ->with(['user', 'attendance'])
                ->where('company_id', $companyId)
                ->whereIn('id', $ids)
                ->get();

            foreach ($regularizations as $regularization) {
                if ($regularization->status !== AttendanceRegularization::STATUS_PENDING) {
                    $skipped++;

                    continue;
                }

                if (! $reviewer?->canApproveRegularization($regularization)) {
                    $skipped++;

                    continue;
                }

                if ($action === 'approve') {
                    $this->applyApproval($regularization, $reviewer, $remarks);
                } else {
                    $this->applyRejection($regularization, $reviewer, $remarks);
                }

                $processed++;
            }

            $skipped += count($ids) - $regularizations->count();
        });

        if ($processed === 0) {
            return $this->redirectAfterReview(
                $request,
                'No requests were processed. They may already be reviewed or you lack permission.',
                'error'
            );
        }

        $verb = $action === 'approve' ? 'approved' : 'rejected';
        $message = "{$processed} request(s) {$verb}.";
        if ($skipped > 0) {
            $message .= " {$skipped} skipped.";
        }

        return $this->redirectAfterReview($request, $message, 'success');
    }

    protected function applyApproval(AttendanceRegularization $attendanceRegularization, User $reviewer, ?string $remarks): void
    {
        $attendanceRegularization->update([
            'status' => AttendanceRegularization::STATUS_APPROVED,
            'reviewed_by' => $reviewer->id,
            'reviewed_at' => now(),
            'review_remarks' => $remarks,
        ]);

        $attendance = null;
        if ($attendanceRegularization->attendance_id && $attendanceRegularization->requested_clock_in) {
            $attendanceRegularization->attendance->update([
                'clock_in_date_time' => $attendanceRegularization->requested_clock_in,
                'clock_out_date_time' => $attendanceRegularization->requested_clock_out ?? $attendanceRegularization->attendance->clock_out_date_time,
            ]);
            $attendance = $attendanceRegularization->attendance->fresh();
        } elseif (! $attendanceRegularization->attendance_id && $attendanceRegularization->requested_clock_in) {
            $attendance = Attendance::create([
                'company_id' => $attendanceRegularization->company_id,
                'user_id' => $attendanceRegularization->user_id,
                'date' => $attendanceRegularization->date,
                'clock_in_date_time' => $attendanceRegularization->requested_clock_in,
                'clock_out_date_time' => $attendanceRegularization->requested_clock_out,
                'status' => 'present',
            ]);
        }

        if ($attendance && $attendanceRegularization->requested_clock_in) {
            $clockIn = $attendanceRegularization->requested_clock_in;
            $clockOut = $attendanceRegularization->requested_clock_out;
            $startTime = $clockIn->format('H:i:s');
            $endTime = $clockOut?->format('H:i:s');

            $session = WorkDuration::query()
                ->where('company_id', $attendance->company_id)
                ->where('attendance_id', $attendance->id)
                ->where('start_time', $startTime)
                ->where('end_time', $endTime)
                ->first();

            if (! $session) {
                WorkDuration::create([
                    'company_id' => $attendance->company_id,
                    'attendance_id' => $attendance->id,
                    'start_time' => $startTime,
                    'end_time' => $endTime,
                    'duration' => $clockOut ? $clockIn->diffInSeconds($clockOut) : null,
                    'status' => $clockOut ? 'closed' : 'open',
                    'notes' => 'Regularization approved',
                ]);
            }

            $attendance->update([
                'clock_in_time' => $clockIn->format('H:i:s'),
                'clock_out_time' => $clockOut?->format('H:i:s'),
                'total_duration' => (int) WorkDuration::query()
                    ->where('company_id', $attendance->company_id)
                    ->where('attendance_id', $attendance->id)
                    ->sum('duration'),
            ]);
        }

        $attendanceRegularization->user?->notify(new AttendanceRegularizationReviewed($attendanceRegularization, 'approved'));
    }

    protected function applyRejection(AttendanceRegularization $attendanceRegularization, User $reviewer, ?string $remarks): void
    {
        $attendanceRegularization->update([
            'status' => AttendanceRegularization::STATUS_REJECTED,
            'reviewed_by' => $reviewer->id,
            'reviewed_at' => now(),
            'review_remarks' => $remarks,
        ]);

        $attendanceRegularization->user?->notify(new AttendanceRegularizationReviewed($attendanceRegularization, 'rejected'));
    }

    /**
     * @return array<string, int|string>
     */
    protected function indexRedirectParams(Request $request): array
    {
        $params = [];

        if ($request->filled('year')) {
            $params['year'] = $request->integer('year');
        }
        if ($request->filled('month')) {
            $params['month'] = $request->integer('month');
        }
        if ($request->filled('status')) {
            $params['status'] = (string) $request->input('status');
        }

        return $params;
    }

    protected function redirectAfterReview(Request $request, string $message, string $type): RedirectResponse
    {
        if ($request->input('redirect_to') === 'attendance') {
            return redirect()->route('attendance.index', array_filter([
                'year' => $request->integer('year') ?: null,
                'month' => $request->integer('month') ?: null,
                'employee_id' => $request->integer('employee_id') ?: null,
            ]))->with($type, $message);
        }

        return redirect()
            ->route('attendance-regularizations.index', $this->indexRedirectParams($request))
            ->with($type, $message);
    }

    protected function normalizeTimeInput(mixed $time): ?string
    {
        if ($time === null || $time === '') {
            return null;
        }

        $time = trim((string) $time);

        if (preg_match('/^\d{2}:\d{2}$/', $time)) {
            return $time;
        }

        if (preg_match('/^(\d{2}:\d{2}):\d{2}$/', $time, $matches)) {
            return $matches[1];
        }

        try {
            return Carbon::parse($time)->format('H:i');
        } catch (\Throwable) {
            return $time;
        }
    }
}
