<?php

namespace App\Services;

use App\Models\TrainingPackage;
use App\Models\TrainingSession;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

/**
 * TrainingService
 *
 * Handles training package and session management operations.
 * Integrates with MeetService for online session creation.
 *
 * @package App\Services
 */
class TrainingService
{
    /**
     * Create a new training package.
     *
     * @param array $data
     * @return TrainingPackage
     * @throws \Exception
     */
    public function createPackage(array $data): TrainingPackage
    {
        return DB::transaction(function () use ($data) {
            // Set the creator
            $data['created_by'] = auth()->id();
            
            // Handle tags
            if (isset($data['tags']) && is_string($data['tags'])) {
                $data['tags'] = array_map('trim', explode(',', $data['tags']));
            }
            
            // Auto-set is_free based on fee_amount
            if (!isset($data['is_free'])) {
                $data['is_free'] = empty($data['fee_amount']) || $data['fee_amount'] == 0;
            }
            
            $package = TrainingPackage::create($data);
            
            Log::info('Training package created', [
                'package_id' => $package->id,
                'name' => $package->name,
                'created_by' => auth()->id(),
            ]);
            
            return $package;
        });
    }

    /**
     * Update a training package.
     *
     * @param TrainingPackage $package
     * @param array $data
     * @return TrainingPackage
     * @throws \Exception
     */
    public function updatePackage(TrainingPackage $package, array $data): TrainingPackage
    {
        return DB::transaction(function () use ($package, $data) {
            // Set the updater
            $data['updated_by'] = auth()->id();
            
            // Handle tags
            if (isset($data['tags']) && is_string($data['tags'])) {
                $data['tags'] = array_map('trim', explode(',', $data['tags']));
            }
            
            // Auto-set is_free based on fee_amount
            if (isset($data['fee_amount']) && !isset($data['is_free'])) {
                $data['is_free'] = empty($data['fee_amount']) || $data['fee_amount'] == 0;
            }
            
            $package->update($data);
            
            Log::info('Training package updated', [
                'package_id' => $package->id,
                'name' => $package->name,
                'updated_by' => auth()->id(),
            ]);
            
            return $package;
        });
    }

    /**
     * Delete a training package.
     *
     * @param TrainingPackage $package
     * @return bool
     * @throws \Exception
     */
    public function deletePackage(TrainingPackage $package): bool
    {
        return DB::transaction(function () use ($package) {
            // Check for active registrations
            $activeRegistrations = $package->registrations()
                ->whereIn('status', ['pending', 'approved'])
                ->count();
            
            if ($activeRegistrations > 0) {
                throw new \Exception(
                    "Cannot delete package with {$activeRegistrations} active registration(s). " .
                    "Please cancel or complete the registrations first."
                );
            }
            
            Log::info('Training package deleted', [
                'package_id' => $package->id,
                'name' => $package->name,
                'deleted_by' => auth()->id(),
            ]);
            
            return $package->delete();
        });
    }

    /**
     * Create a new training session.
     *
     * @param TrainingPackage $package
     * @param array $data
     * @return TrainingSession
     * @throws \Exception
     */
    public function createSession(TrainingPackage $package, array $data): TrainingSession
    {
        return DB::transaction(function () use ($package, $data) {
            $data['training_package_id'] = $package->id;
            $data['created_by'] = auth()->id();
            
            // Validate session doesn't conflict with existing sessions
            $this->validateSessionSchedule($data);
            
            $session = TrainingSession::create($data);
            
            // If online session, create Google Meet link
            if ($session->session_type === 'online' || $session->session_type === 'hybrid') {
                $this->createOnlineMeeting($session);
            }
            
            Log::info('Training session created', [
                'session_id' => $session->id,
                'package_id' => $package->id,
                'session_name' => $session->session_name,
                'created_by' => auth()->id(),
            ]);
            
            return $session;
        });
    }

    /**
     * Update a training session.
     *
     * @param TrainingSession $session
     * @param array $data
     * @return TrainingSession
     * @throws \Exception
     */
    public function updateSession(TrainingSession $session, array $data): TrainingSession
    {
        return DB::transaction(function () use ($session, $data) {
            $oldDate = $session->session_date;
            $oldTime = $session->start_time;
            
            // Validate session doesn't conflict with existing sessions
            $this->validateSessionSchedule($data, $session->id);
            
            $session->update($data);
            
            // If date/time changed, notify enrolled participants
            if ($oldDate != $session->session_date || $oldTime != $session->start_time) {
                $this->notifyScheduleChange($session);
            }
            
            // If online session and no meeting link, create one
            if (($session->session_type === 'online' || $session->session_type === 'hybrid') 
                && !$session->meeting_link) {
                $this->createOnlineMeeting($session);
            }
            
            Log::info('Training session updated', [
                'session_id' => $session->id,
                'updated_by' => auth()->id(),
            ]);
            
            return $session;
        });
    }

    /**
     * Cancel a training session.
     *
     * @param TrainingSession $session
     * @param string $reason
     * @return bool
     * @throws \Exception
     */
    public function cancelSession(TrainingSession $session, string $reason): bool
    {
        return DB::transaction(function () use ($session, $reason) {
            $session->cancel($reason);
            
            // Notify all enrolled participants
            $this->notifyCancellation($session, $reason);
            
            Log::info('Training session cancelled', [
                'session_id' => $session->id,
                'reason' => $reason,
                'cancelled_by' => auth()->id(),
            ]);
            
            return true;
        });
    }

    /**
     * Assign trainer to session.
     *
     * @param TrainingSession $session
     * @param int $trainerId
     * @param string $role
     * @param string|null $topic
     * @return void
     */
    public function assignTrainer(
        TrainingSession $session, 
        int $trainerId, 
        string $role = 'lead', 
        ?string $topic = null
    ): void {
        DB::transaction(function () use ($session, $trainerId, $role, $topic) {
            // Check if trainer is already assigned
            $existingAssignment = $session->trainers()
                ->where('trainer_id', $trainerId)
                ->first();
            
            if ($existingAssignment) {
                // Update existing assignment
                $session->trainers()->updateExistingPivot($trainerId, [
                    'role' => $role,
                    'topic' => $topic,
                    'confirmation_status' => 'pending',
                ]);
            } else {
                // Create new assignment
                $session->trainers()->attach($trainerId, [
                    'role' => $role,
                    'topic' => $topic,
                    'confirmation_status' => 'pending',
                ]);
            }
            
            Log::info('Trainer assigned to session', [
                'session_id' => $session->id,
                'trainer_id' => $trainerId,
                'role' => $role,
                'assigned_by' => auth()->id(),
            ]);
        });
    }

    /**
     * Remove trainer from session.
     *
     * @param TrainingSession $session
     * @param int $trainerId
     * @return bool
     */
    public function removeTrainer(TrainingSession $session, int $trainerId): bool
    {
        $detached = $session->trainers()->detach($trainerId);
        
        if ($detached > 0) {
            Log::info('Trainer removed from session', [
                'session_id' => $session->id,
                'trainer_id' => $trainerId,
                'removed_by' => auth()->id(),
            ]);
        }
        
        return $detached > 0;
    }

    /**
     * Validate session schedule doesn't conflict.
     *
     * @param array $data
     * @param int|null $excludeSessionId
     * @return void
     * @throws \Exception
     */
    protected function validateSessionSchedule(array $data, ?int $excludeSessionId = null): void
    {
        // Check for venue conflicts for in-person sessions
        if (!empty($data['venue_name']) && in_array($data['session_type'] ?? 'in_person', ['in_person', 'hybrid'])) {
            $conflict = TrainingSession::where('venue_name', $data['venue_name'])
                ->where('session_date', $data['session_date'])
                ->where(function ($query) use ($data) {
                    $query->whereBetween('start_time', [$data['start_time'], $data['end_time']])
                        ->orWhereBetween('end_time', [$data['start_time'], $data['end_time']]);
                })
                ->when($excludeSessionId, fn($q) => $q->where('id', '!=', $excludeSessionId))
                ->exists();
            
            if ($conflict) {
                throw new \Exception('The venue is already booked for the selected date and time.');
            }
        }
    }

    /**
     * Create online meeting link.
     *
     * @param TrainingSession $session
     * @return void
     */
    protected function createOnlineMeeting(TrainingSession $session): void
    {
        try {
            $meetService = app(MeetService::class);
            
            // Get enrolled participants
            $attendees = $session->trainingPackage->registrations()
                ->whereIn('status', ['approved', 'completed'])
                ->with('user')
                ->get()
                ->pluck('user.email')
                ->filter()
                ->unique()
                ->values()
                ->toArray();
            
            $result = $meetService->createMeeting(
                summary: $session->trainingPackage->name . ' - ' . $session->session_name,
                startTime: $session->session_date->format('Y-m-d') . 'T' . $session->start_time,
                endTime: $session->session_date->format('Y-m-d') . 'T' . $session->end_time,
                attendees: $attendees,
                description: $session->description
            );
            
            if ($result['success']) {
                $session->update([
                    'meeting_link' => $result['meeting_link'],
                    'meeting_id' => $result['meeting_id'] ?? null,
                    'meeting_provider' => 'google_meet',
                ]);
                
                Log::info('Google Meet created for training session', [
                    'session_id' => $session->id,
                    'meeting_link' => $result['meeting_link'],
                ]);
            }
        } catch (\Exception $e) {
            Log::error('Failed to create Google Meet for training session', [
                'session_id' => $session->id,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Notify participants of schedule change.
     *
     * @param TrainingSession $session
     * @return void
     */
    protected function notifyScheduleChange(TrainingSession $session): void
    {
        try {
            $notificationService = app(NotificationService::class);
            $notificationService->sendScheduleChangeNotification($session);
        } catch (\Exception $e) {
            Log::error('Failed to send schedule change notification', [
                'session_id' => $session->id,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Notify participants of cancellation.
     *
     * @param TrainingSession $session
     * @param string $reason
     * @return void
     */
    protected function notifyCancellation(TrainingSession $session, string $reason): void
    {
        try {
            $notificationService = app(NotificationService::class);
            $notificationService->sendCancellationNotification($session, $reason);
        } catch (\Exception $e) {
            Log::error('Failed to send cancellation notification', [
                'session_id' => $session->id,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Get calendar events for a date range.
     *
     * @param string $startDate
     * @param string $endDate
     * @return array
     */
    public function getCalendarEvents(string $startDate, string $endDate): array
    {
        $sessions = TrainingSession::with(['trainingPackage'])
            ->whereBetween('session_date', [$startDate, $endDate])
            ->orderBy('session_date')
            ->orderBy('start_time')
            ->get();
        
        return $sessions->map(function ($session) {
            return [
                'id' => $session->id,
                'title' => $session->session_name,
                'start' => $session->session_date->format('Y-m-d') . 'T' . $session->start_time,
                'end' => $session->session_date->format('Y-m-d') . 'T' . $session->end_time,
                'backgroundColor' => $this->getStatusColor($session->status),
                'borderColor' => $this->getStatusColor($session->status),
                'url' => route('training.sessions.show', $session),
                'extendedProps' => [
                    'package_name' => $session->trainingPackage->name,
                    'session_type' => $session->session_type,
                    'venue' => $session->venue_name,
                    'status' => $session->status,
                ],
            ];
        })->toArray();
    }

    /**
     * Get status color for calendar.
     *
     * @param string $status
     * @return string
     */
    protected function getStatusColor(string $status): string
    {
        return match($status) {
            'scheduled' => '#3b82f6', // blue
            'ongoing' => '#10b981', // green
            'completed' => '#6b7280', // gray
            'cancelled' => '#ef4444', // red
            'postponed' => '#f59e0b', // amber
            default => '#6b7280',
        };
    }
}
