<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Document;
use App\Models\Leave;
use App\Models\LeaveType;
use App\Models\User;
use App\Services\Leave\LeaveLedgerService;
use App\Services\Leave\LeaveRuleValidator;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class LeaveController extends Controller
{
    public function index(Request $request)
    {
        $authUser = Auth::user();
        $companyId = $authUser?->company_id;
        $visibleUserIds = $authUser?->visibleStaffUserIds() ?? [];
        $query = Leave::with(['user', 'leaveType']);
        if ($companyId) {
            $query->where('company_id', $companyId);
        }
        if (! empty($visibleUserIds)) {
            $query->whereIn('user_id', $visibleUserIds);
        }
        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }

        $leaves = $query->orderByDesc('created_at')->paginate(15)->withQueryString();
        $today = now()->toDateString();
        $baseCountQuery = Leave::query();
        if ($companyId) {
            $baseCountQuery->where('company_id', $companyId);
        }
        if (! empty($visibleUserIds)) {
            $baseCountQuery->whereIn('user_id', $visibleUserIds);
        }

        $stats = [
            'pending' => (clone $baseCountQuery)->where('status', 'pending')->count(),
            'approved_today' => (clone $baseCountQuery)->where('status', 'approved')->whereDate('updated_at', $today)->count(),
            'rejected' => (clone $baseCountQuery)->where('status', 'rejected')->count(),
            'on_leave_today' => (clone $baseCountQuery)->where('status', 'approved')->whereDate('start_date', '<=', $today)->whereDate('end_date', '>=', $today)->count(),
        ];

        return view('leaves.index', compact('leaves', 'stats'));
    }

    public function create()
    {
        $authUser = Auth::user();
        $companyId = $authUser?->company_id;
        $visibleUserIds = $authUser?->visibleStaffUserIds() ?? [];
        $users = User::query()->forActiveDirectory();
        $leaveTypes = LeaveType::query();

        if ($companyId) {
            $users->where('company_id', $companyId);
            $leaveTypes->where('company_id', $companyId);
        }
        if (! empty($visibleUserIds)) {
            $users->whereIn('id', $visibleUserIds);
        }

        return view('leaves.create', [
            'users' => $users->orderBy('name')->get(),
            'leaveTypes' => $leaveTypes->orderBy('name')->get(),
        ]);
    }

    public function store(Request $request, LeaveRuleValidator $ruleValidator, LeaveLedgerService $ledger)
    {
        $authUser = Auth::user();
        $companyId = $authUser?->company_id;
        $visibleUserIds = $authUser?->visibleStaffUserIds() ?? [];
        $validated = $request->validate([
            'user_id' => [
                'required',
                Rule::exists('users', 'id')->where(function ($q) use ($companyId) {
                    $q->where('user_type', User::USER_TYPE_STAFF);
                    if ($companyId) {
                        $q->where('company_id', $companyId);
                    }
                }),
                Rule::in($visibleUserIds),
            ],
            'leave_type_id' => [
                'required',
                Rule::exists('leave_types', 'id')->where(function ($q) use ($companyId) {
                    if ($companyId) {
                        $q->where('company_id', $companyId);
                    }
                }),
            ],
            'start_date' => ['required', 'date'],
            'end_date' => ['required', 'date', 'after_or_equal:start_date'],
            'reason' => ['required', 'string', 'max:1000'],
            'is_half_day' => ['nullable', 'boolean'],
            'half_leave' => ['nullable', Rule::in(['first_half', 'second_half'])],
            'is_paid' => ['nullable', 'boolean'],
            'attachment' => ['nullable', 'file', 'max:10240'],
        ]);

        $startDate = \Carbon\Carbon::parse($validated['start_date']);
        $endDate = \Carbon\Carbon::parse($validated['end_date']);

        $isHalfDay = (bool) ($validated['is_half_day'] ?? false);
        $halfLeave = $isHalfDay ? ($validated['half_leave'] ?? 'first_half') : 'before_break';

        $user = User::query()
            ->forActiveDirectory()
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->whereIn('id', $visibleUserIds)
            ->findOrFail($validated['user_id']);

        $leaveType = LeaveType::query()
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->findOrFail($validated['leave_type_id']);

        $calc = $ruleValidator->validateForCreate($request, $user, $leaveType, $startDate, $endDate, $isHalfDay);

        $leave = Leave::create([
            'company_id' => $companyId,
            'user_id' => $validated['user_id'],
            'leave_type_id' => $validated['leave_type_id'],
            'start_date' => $validated['start_date'],
            'end_date' => $validated['end_date'],
            'total_days' => $calc['chargeable_days'],
            'is_half_day' => $isHalfDay,
            'half_leave' => $halfLeave,
            'reason' => trim((string) $validated['reason']) !== '' ? $validated['reason'] : 'N/A',
            'remark' => null,
            'is_paid' => (bool) ($validated['is_paid'] ?? false),
            'status' => 'pending',
        ]);

        try {
            $ledger->holdPending($leave);
        } catch (\InvalidArgumentException $e) {
            $leave->delete();

            return back()->withInput()->withErrors(['start_date' => $e->getMessage()]);
        }

        if ($request->file('attachment')) {
            $file = $request->file('attachment');
            $path = $file->store('documents/' . ($companyId ?? 'shared'), 'public');
            Document::create([
                'company_id' => $companyId,
                'name' => 'Leave Attachment - ' . ($user->employee_number ?: ('EMP' . $user->id)) . ' - ' . now()->format('YmdHis'),
                'type' => 'leave_attachment',
                'path' => $path,
                'documentable_type' => Leave::class,
                'documentable_id' => $leave->id,
                'uploaded_by' => Auth::id(),
            ]);
        }

        return redirect()->route('leaves.index')->with('success', 'Leave request created successfully.');
    }

    public function updateStatus(Request $request, Leave $leave, LeaveLedgerService $ledger)
    {
        $companyId = Auth::user()?->company_id;
        if ($companyId && $leave->company_id !== $companyId) {
            abort(403);
        }

        $validated = $request->validate([
            'status' => ['required', Rule::in(['approved', 'rejected'])],
            'remark' => ['nullable', 'string', 'max:1000'],
        ]);

        $previousStatus = $leave->status;

        $leave->update([
            'status' => $validated['status'],
            'remark' => trim((string) ($validated['remark'] ?? '')) !== '' ? $validated['remark'] : null,
        ]);

        if ($previousStatus === 'pending' && $validated['status'] === 'approved') {
            $ledger->consumePending($leave);
        } elseif ($previousStatus === 'pending' && $validated['status'] === 'rejected') {
            $ledger->releasePending($leave);
        } elseif ($previousStatus === 'approved' && $validated['status'] === 'rejected') {
            $ledger->reverseApproved($leave);
        }

        return redirect()->back()->with('success', 'Leave status updated successfully.');
    }

    public function dashboard(LeaveLedgerService $ledger)
    {
        $authUser = Auth::user();
        $companyId = $authUser?->company_id;
        $year = $ledger->leaveYear();
        $visibleUserIds = $authUser?->visibleStaffUserIds() ?? [];

        $balances = $ledger->balancesForUser($authUser, $year);

        $pendingQuery = Leave::with(['user', 'leaveType'])
            ->where('status', 'pending');
        if ($companyId) {
            $pendingQuery->where('company_id', $companyId);
        }
        if (! empty($visibleUserIds)) {
            $pendingQuery->whereIn('user_id', $visibleUserIds);
        }
        $pendingLeaves = $pendingQuery->orderByDesc('created_at')->limit(10)->get();

        $teamBalances = [];
        if ($authUser?->isTenantManager()) {
            $teamUsers = User::query()
                ->forActiveDirectory()
                ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
                ->when(! empty($visibleUserIds), fn ($q) => $q->whereIn('id', $visibleUserIds))
                ->orderBy('name')
                ->limit(50)
                ->get();

            foreach ($teamUsers as $teamUser) {
                $teamBalances[] = [
                    'user' => $teamUser,
                    'balances' => $ledger->balancesForUser($teamUser, $year),
                ];
            }
        }

        return view('leaves.dashboard', compact('balances', 'pendingLeaves', 'teamBalances', 'year'));
    }

    public function balanceLookup(Request $request, LeaveLedgerService $ledger)
    {
        $companyId = Auth::user()?->company_id;
        $validated = $request->validate([
            'user_id' => ['required', 'integer'],
            'leave_type_id' => ['required', 'integer'],
        ]);

        $user = User::query()
            ->forActiveDirectory()
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->findOrFail($validated['user_id']);

        $leaveType = LeaveType::query()
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->findOrFail($validated['leave_type_id']);

        $available = $ledger->availableLeaveDays($user, $leaveType);

        return response()->json([
            'available' => $available === PHP_FLOAT_MAX ? null : round($available, 2),
            'is_unlimited' => $leaveType->is_unlimited,
        ]);
    }

    public function leavePopup(Request $request, LeaveLedgerService $ledger)
    {
        $authUser = Auth::user();
        $companyId = $authUser?->company_id;

        $leaveId = $request->query('leave_id');
        $leave = Leave::with(['user', 'leaveType', 'documents'])
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->findOrFail($leaveId);

        $availableLeaves = $ledger->availableLeaveDays($leave->user, $leave->leaveType);
        if ($availableLeaves === PHP_FLOAT_MAX) {
            $availableLeaves = null;
        }
 
        return view('leaves.leave-popup', compact('leave', 'availableLeaves'));
    }
}
