<?php

namespace App\Http\Controllers\Training;

use App\Http\Controllers\Controller;
use App\Http\Requests\Training\StoreTrainingSessionRequest;
use App\Models\TrainingSession;
use App\Models\TrainingPackage;
use App\Models\User;
use App\Services\TrainingService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;

/**
 * SessionController
 *
 * Handles training session management for authenticated users.
 *
 * @package App\Http\Controllers\Training
 */
class SessionController extends Controller
{
    /**
     * TrainingService instance.
     *
     * @var \App\Services\TrainingService
     */
    protected TrainingService $trainingService;

    /**
     * Create a new SessionController instance.
     *
     * @param \App\Services\TrainingService $trainingService
     */
    public function __construct(TrainingService $trainingService)
    {
        $this->trainingService = $trainingService;
    }

    /**
     * Display a listing of training sessions.
     *
     * @return \Illuminate\View\View
     */
    public function index(): View
    {
        abort_unless(auth()->user()->can('view training sessions'), 403);

        $sessions = TrainingSession::with(['trainingPackage', 'trainers'])
            ->when(request('status'), function ($query, $status) {
                $query->where('status', $status);
            })
            ->when(request('package'), function ($query, $packageId) {
                $query->where('training_package_id', $packageId);
            })
            ->when(request('from'), function ($query, $from) {
                $query->where('session_date', '>=', $from);
            })
            ->when(request('to'), function ($query, $to) {
                $query->where('session_date', '<=', $to);
            })
            ->orderBy('session_date')
            ->orderBy('start_time')
            ->paginate(20);

        $packages = TrainingPackage::active()->orderBy('name')->get();
        $statistics = $this->getStatistics();

        return view('training.sessions.index', compact('sessions', 'packages', 'statistics'));
    }

    /**
     * Display the session calendar view.
     *
     * @return \Illuminate\View\View
     */
    public function calendar(): View
    {
        abort_unless(auth()->user()->can('view training sessions'), 403);

        $packages = TrainingPackage::active()->orderBy('name')->get();

        // Load all sessions for the calendar
        $sessions = TrainingSession::with(['trainingPackage'])
            ->whereYear('session_date', now()->year)
            ->orderBy('session_date')
            ->get();

        $todaySessions = TrainingSession::with(['trainingPackage'])
            ->whereDate('session_date', today())
            ->whereIn('status', ['scheduled', 'ongoing'])
            ->orderBy('start_time')
            ->get();

        $weekSessions = TrainingSession::with(['trainingPackage'])
            ->whereBetween('session_date', [now()->startOfWeek()->toDateString(), now()->endOfWeek()->toDateString()])
            ->whereIn('status', ['scheduled', 'ongoing'])
            ->orderBy('session_date')
            ->orderBy('start_time')
            ->get();

        $statistics = [
            'this_week' => $weekSessions->count(),
            'this_month' => TrainingSession::whereMonth('session_date', now()->month)
                ->whereYear('session_date', now()->year)
                ->count(),
            'online' => TrainingSession::whereIn('session_type', ['online', 'hybrid'])
                ->whereYear('session_date', now()->year)
                ->count(),
            'physical' => TrainingSession::where('session_type', 'in_person')
                ->whereYear('session_date', now()->year)
                ->count(),
        ];

        $currentMonth = now()->format('F Y');
        $currentYear = now()->year;
        $currentMonthNum = now()->month;

        return view('training.sessions.calendar', compact(
            'packages', 'sessions', 'todaySessions', 'weekSessions',
            'statistics', 'currentMonth', 'currentYear', 'currentMonthNum'
        ));
    }

    /**
     * Get calendar events as JSON.
     *
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function calendarEvents(Request $request): JsonResponse
    {
        abort_unless(auth()->user()->can('view training sessions'), 403);

        $start = $request->get('start', now()->startOfMonth()->toDateString());
        $end = $request->get('end', now()->endOfMonth()->toDateString());

        $events = $this->trainingService->getCalendarEvents($start, $end);

        return response()->json($events);
    }

    /**
     * API endpoint for calendar events.
     *
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function apiCalendarEvents(Request $request): JsonResponse
    {
        $start = $request->get('start', now()->startOfMonth()->toDateString());
        $end = $request->get('end', now()->endOfMonth()->toDateString());

        $events = $this->trainingService->getCalendarEvents($start, $end);

        return response()->json($events);
    }

    /**
     * Show the form for creating a new training session.
     *
     * @return \Illuminate\View\View
     */
    public function create(): View
    {
        abort_unless(auth()->user()->can('create training sessions'), 403);

        $packages = TrainingPackage::active()->orderBy('name')->get();
        $trainers = User::role('Trainers')->orderBy('name')->get();

        return view('training.sessions.create', compact('packages', 'trainers'));
    }

    /**
     * Store a newly created training session.
     *
     * @param \App\Http\Requests\Training\StoreTrainingSessionRequest $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function store(StoreTrainingSessionRequest $request): RedirectResponse
    {
        abort_unless(auth()->user()->can('create training sessions'), 403);

        $validated = $request->validated();

        try {
            $package = TrainingPackage::findOrFail($validated['training_package_id']);
            $session = $this->trainingService->createSession($package, $validated);

            // Assign trainers — form sends trainers[] as flat array of IDs
            $trainerIds = $request->input('trainers', []);
            foreach ($trainerIds as $trainerId) {
                $this->trainingService->assignTrainer($session, (int) $trainerId, 'lead', null);
            }

            return redirect()
                ->route('training.sessions.show', $session)
                ->with('success', 'Training session created successfully!');

        } catch (\Exception $e) {
            return back()
                ->withInput()
                ->with('error', 'Failed to create session: ' . $e->getMessage());
        }
    }

    /**
     * Display the specified training session.
     *
     * @param \App\Models\TrainingSession $session
     * @return \Illuminate\View\View
     */
    public function show(TrainingSession $session): View
    {
        abort_unless(auth()->user()->can('view training sessions'), 403);

        $session->load([
            'trainingPackage',
            'trainers',
            'attendances.user',
        ]);

        $registrations = $session->trainingPackage->registrations()
            ->whereIn('status', ['approved', 'completed'])
            ->with('user')
            ->get();

        $attended = $session->attendances()->where('attendance_status', 'present')->count();
        $absent = $registrations->count() - $attended;
        $attendanceRate = $registrations->count() > 0
            ? round(($attended / $registrations->count()) * 100)
            : 0;

        return view('training.sessions.show', compact('session', 'registrations', 'attended', 'absent', 'attendanceRate'));
    }

    /**
     * Show the form for editing the specified training session.
     *
     * @param \App\Models\TrainingSession $session
     * @return \Illuminate\View\View
     */
    public function edit(TrainingSession $session): View
    {
        abort_unless(auth()->user()->can('edit training sessions'), 403);

        $packages = TrainingPackage::active()->orderBy('name')->get();
        $trainers = User::role('Trainers')->orderBy('name')->get();
        $session->load('trainers');

        return view('training.sessions.edit', compact('session', 'packages', 'trainers'));
    }

    /**
     * Update the specified training session.
     *
     * @param \App\Http\Requests\Training\StoreTrainingSessionRequest $request
     * @param \App\Models\TrainingSession $session
     * @return \Illuminate\Http\RedirectResponse
     */
    public function update(StoreTrainingSessionRequest $request, TrainingSession $session): RedirectResponse
    {
        abort_unless(auth()->user()->can('edit training sessions'), 403);

        $validated = $request->validated();

        try {
            $this->trainingService->updateSession($session, $validated);

            // Sync trainers — detach all then re-attach selected
            $trainerIds = $request->input('trainers', []);
            $session->trainers()->detach();
            foreach ($trainerIds as $trainerId) {
                $this->trainingService->assignTrainer($session, (int) $trainerId, 'lead', null);
            }

            return redirect()
                ->route('training.sessions.show', $session)
                ->with('success', 'Training session updated successfully!');

        } catch (\Exception $e) {
            return back()
                ->withInput()
                ->with('error', 'Failed to update session: ' . $e->getMessage());
        }
    }

    /**
     * Remove the specified training session.
     *
     * @param \App\Models\TrainingSession $session
     * @return \Illuminate\Http\RedirectResponse
     */
    public function destroy(TrainingSession $session): RedirectResponse
    {
        abort_unless(auth()->user()->can('delete training sessions'), 403);

        try {
            $packageId = $session->training_package_id;
            $session->delete();

            return redirect()
                ->route('training.sessions.index', ['package' => $packageId])
                ->with('success', 'Training session deleted successfully!');

        } catch (\Exception $e) {
            return back()
                ->with('error', 'Failed to delete session: ' . $e->getMessage());
        }
    }

    /**
     * Cancel a training session.
     *
     * @param \Illuminate\Http\Request $request
     * @param \App\Models\TrainingSession $session
     * @return \Illuminate\Http\RedirectResponse
     */
    public function cancel(Request $request, TrainingSession $session): RedirectResponse
    {
        abort_unless(auth()->user()->can('cancel training sessions'), 403);

        $validated = $request->validate([
            'cancellation_reason' => 'required|string|max:500',
        ]);

        try {
            $this->trainingService->cancelSession($session, $validated['cancellation_reason']);

            return back()->with('success', 'Session cancelled successfully. All participants have been notified.');

        } catch (\Exception $e) {
            return back()->with('error', 'Failed to cancel session: ' . $e->getMessage());
        }
    }

    /**
     * Postpone a training session.
     *
     * @param \Illuminate\Http\Request $request
     * @param \App\Models\TrainingSession $session
     * @return \Illuminate\Http\RedirectResponse
     */
    public function postpone(Request $request, TrainingSession $session): RedirectResponse
    {
        abort_unless(auth()->user()->can('schedule training sessions'), 403);

        $validated = $request->validate([
            'new_date' => 'required|date|after:today',
            'postpone_reason' => 'required|string|max:500',
        ]);

        try {
            $session->postpone($validated['new_date'], $validated['postpone_reason']);

            return back()->with('success', 'Session postponed successfully. All participants have been notified.');

        } catch (\Exception $e) {
            return back()->with('error', 'Failed to postpone session: ' . $e->getMessage());
        }
    }

    /**
     * Mark a session as completed.
     *
     * @param \App\Models\TrainingSession $session
     * @return \Illuminate\Http\RedirectResponse
     */
    public function markCompleted(TrainingSession $session): RedirectResponse
    {
        abort_unless(auth()->user()->can('edit training sessions'), 403);

        try {
            $session->markCompleted();

            return back()->with('success', 'Session marked as completed.');

        } catch (\Exception $e) {
            return back()->with('error', 'Failed to mark session as completed: ' . $e->getMessage());
        }
    }

    /**
     * Mark a session as started (ongoing).
     *
     * @param \App\Models\TrainingSession $session
     * @return \Illuminate\Http\RedirectResponse
     */
    public function markStarted(TrainingSession $session): RedirectResponse
    {
        abort_unless(auth()->user()->can('edit training sessions'), 403);

        try {
            $session->update(['status' => 'ongoing']);

            return back()->with('success', 'Session started successfully.');

        } catch (\Exception $e) {
            return back()->with('error', 'Failed to start session: ' . $e->getMessage());
        }
    }

    /**
     * Assign a trainer to a session.
     *
     * @param \Illuminate\Http\Request $request
     * @param \App\Models\TrainingSession $session
     * @return \Illuminate\Http\RedirectResponse
     */
    public function assignTrainer(Request $request, TrainingSession $session): RedirectResponse
    {
        abort_unless(auth()->user()->can('assign trainers'), 403);

        $validated = $request->validate([
            'trainer_id' => 'required|exists:users,id',
            'role' => 'required|in:lead,assistant',
            'topic' => 'nullable|string|max:191',
        ]);

        try {
            $this->trainingService->assignTrainer(
                $session,
                $validated['trainer_id'],
                $validated['role'],
                $validated['topic'] ?? null
            );

            return back()->with('success', 'Trainer assigned successfully!');

        } catch (\Exception $e) {
            return back()->with('error', 'Failed to assign trainer: ' . $e->getMessage());
        }
    }

    /**
     * Remove a trainer from a session.
     *
     * @param \App\Models\TrainingSession $session
     * @param \App\Models\User $trainer
     * @return \Illuminate\Http\RedirectResponse
     */
    public function removeTrainer(TrainingSession $session, User $trainer): RedirectResponse
    {
        abort_unless(auth()->user()->can('remove trainers'), 403);

        try {
            $this->trainingService->removeTrainer($session, $trainer->id);

            return back()->with('success', 'Trainer removed from session.');

        } catch (\Exception $e) {
            return back()->with('error', 'Failed to remove trainer: ' . $e->getMessage());
        }
    }

    /**
     * Get session statistics.
     *
     * @return array
     */
    protected function getStatistics(): array
    {
        return [
            'total' => TrainingSession::count(),
            'scheduled' => TrainingSession::where('status', 'scheduled')->count(),
            'ongoing' => TrainingSession::where('status', 'ongoing')->count(),
            'completed' => TrainingSession::where('status', 'completed')->count(),
            'cancelled' => TrainingSession::where('status', 'cancelled')->count(),
            'this_week' => TrainingSession::whereBetween('session_date', [
                now()->startOfWeek()->toDateString(),
                now()->endOfWeek()->toDateString(),
            ])->count(),
        ];
    }
}
