<?php

namespace App\Services\Mentorship;

use App\Models\Mentorship\Assignment;
use App\Models\Mentorship\Application;
use App\Models\Mentorship\Programme;
use App\Models\User;
use App\Services\EmailService;
use App\Services\SMSService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class AssignmentService
{
    protected EmailService $emailService;
    protected SMSService $smsService;

    public function __construct(EmailService $emailService, SMSService $smsService)
    {
        $this->emailService = $emailService;
        $this->smsService = $smsService;
    }

    /**
     * Create a new assignment.
     */
    public function create(array $data): Assignment
    {
        DB::beginTransaction();
        
        try {
            // Validate mentor and mentee
            $mentor = User::findOrFail($data['mentor_id']);
            $mentee = User::findOrFail($data['mentee_id']);
            $programme = Programme::findOrFail($data['programme_id']);

            // Check if assignment already exists
            $existingAssignment = Assignment::where('programme_id', $programme->id)
                ->where('mentor_id', $mentor->id)
                ->where('mentee_id', $mentee->id)
                ->whereIn('status', ['pending_acceptance', 'active'])
                ->first();

            if ($existingAssignment) {
                throw new \Exception('This mentor-mentee pair is already assigned in this programme.');
            }

            // Check mentor capacity
            if ($programme->max_mentees > 0) {
                $currentMentees = Assignment::where('programme_id', $programme->id)
                    ->where('mentor_id', $mentor->id)
                    ->whereIn('status', ['pending_acceptance', 'active'])
                    ->count();

                if ($currentMentees >= $programme->max_mentees) {
                    throw new \Exception('This mentor has reached their maximum mentee capacity.');
                }
            }

            // Set assignment metadata
            $data['assigned_by'] = auth()->id();
            $data['assigned_at'] = now();
            $data['status'] = Assignment::STATUS_PENDING_ACCEPTANCE;
            $data['sessions_total'] = $programme->sessions_count;

            // Create milestones from programme objectives
            if (empty($data['milestones']) && $programme->objectives) {
                $data['milestones'] = $this->generateMilestones($programme);
            }

            $assignment = Assignment::create($data);

            // Update programme statistics
            $programme->updateStatistics();

            DB::commit();

            // Send notification to mentor and mentee
            $this->sendAssignmentNotification($assignment);

            Log::info('Mentorship assignment created', [
                'assignment_id' => $assignment->id,
                'programme_id' => $programme->id,
                'mentor_id' => $mentor->id,
                'mentee_id' => $mentee->id,
                'assigned_by' => auth()->id(),
            ]);

            return $assignment;
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to create mentorship assignment', [
                'error' => $e->getMessage(),
                'data' => $data,
            ]);
            throw $e;
        }
    }

    /**
     * Auto-match mentees with mentors based on criteria.
     */
    public function autoMatch(Programme $programme, array $options = []): array
    {
        DB::beginTransaction();
        
        try {
            $matches = [];
            $unmatched = [];

            // Get unassigned approved mentees
            $menteeApplications = Application::where('programme_id', $programme->id)
                ->where('role_preference', 'mentee')
                ->where('status', 'approved')
                ->whereDoesntHave('menteeAssignment', function ($q) {
                    $q->whereIn('status', ['pending_acceptance', 'active']);
                })
                ->get();

            // Get available mentors
            $mentorApplications = Application::where('programme_id', $programme->id)
                ->where('role_preference', 'mentor')
                ->where('status', 'approved')
                ->get();

            foreach ($menteeApplications as $menteeApp) {
                $bestMatch = null;
                $bestScore = 0;

                foreach ($mentorApplications as $mentorApp) {
                    // Check mentor capacity
                    $currentMentees = Assignment::where('programme_id', $programme->id)
                        ->where('mentor_id', $mentorApp->user_id)
                        ->whereIn('status', ['pending_acceptance', 'active'])
                        ->count();

                    if ($programme->max_mentees > 0 && $currentMentees >= $programme->max_mentees) {
                        continue;
                    }

                    // Calculate match score
                    $score = $this->calculateMatchScore($mentorApp, $menteeApp);

                    if ($score > $bestScore) {
                        $bestScore = $score;
                        $bestMatch = $mentorApp;
                    }
                }

                if ($bestMatch && $bestScore >= ($options['min_score'] ?? 50)) {
                    // Create assignment
                    $assignment = $this->create([
                        'programme_id' => $programme->id,
                        'mentor_id' => $bestMatch->user_id,
                        'mentee_id' => $menteeApp->user_id,
                        'mentor_application_id' => $bestMatch->id,
                        'mentee_application_id' => $menteeApp->id,
                        'matching_criteria' => "Auto-matched with score: {$bestScore}%",
                    ]);

                    $matches[] = [
                        'assignment' => $assignment,
                        'score' => $bestScore,
                    ];
                } else {
                    $unmatched[] = $menteeApp;
                }
            }

            DB::commit();

            Log::info('Auto-matching completed', [
                'programme_id' => $programme->id,
                'matched' => count($matches),
                'unmatched' => count($unmatched),
            ]);

            return [
                'matched' => $matches,
                'unmatched' => $unmatched,
            ];
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Auto-matching failed', [
                'error' => $e->getMessage(),
                'programme_id' => $programme->id,
            ]);
            throw $e;
        }
    }

    /**
     * Calculate match score between mentor and mentee.
     */
    protected function calculateMatchScore(Application $mentorApp, Application $menteeApp): int
    {
        $score = 0;

        // Match by areas of interest (40 points)
        $mentorInterests = collect($mentorApp->areas_of_interest ?? []);
        $menteeInterests = collect($menteeApp->areas_of_interest ?? []);
        
        if ($mentorInterests->isNotEmpty() && $menteeInterests->isNotEmpty()) {
            $commonInterests = $mentorInterests->intersect($menteeInterests)->count();
            $totalInterests = $menteeInterests->count();
            $score += ($commonInterests / $totalInterests) * 40;
        }

        // Match by skills (30 points)
        $mentorSkills = collect($mentorApp->skills ?? []);
        $menteeGoals = collect($menteeApp->learning_goals ?? []);
        
        if ($mentorSkills->isNotEmpty() && $menteeGoals->isNotEmpty()) {
            // Simple keyword matching
            $relevantSkills = 0;
            foreach ($mentorSkills as $skill) {
                foreach ($menteeGoals as $goal) {
                    if (stripos($goal, $skill) !== false) {
                        $relevantSkills++;
                        break;
                    }
                }
            }
            $score += min(($relevantSkills / $mentorSkills->count()) * 30, 30);
        }

        // Match by availability (20 points)
        if ($mentorApp->availability && $menteeApp->time_commitment) {
            // Simple availability matching logic
            $score += 20;
        }

        // Match by category preference (10 points)
        if ($mentorApp->category_id && $menteeApp->category_id) {
            if ($mentorApp->category_id === $menteeApp->category_id) {
                $score += 10;
            }
        }

        return (int) $score;
    }

    /**
     * Accept assignment (as mentor or mentee).
     */
    public function accept(Assignment $assignment, User $user, string $role): Assignment
    {
        DB::beginTransaction();
        
        try {
            if ($role === 'mentor') {
                $assignment->acceptAsMentor();
            } else {
                $assignment->acceptAsMentee();
            }

            DB::commit();

            Log::info('Assignment accepted', [
                'assignment_id' => $assignment->id,
                'user_id' => $user->id,
                'role' => $role,
            ]);

            return $assignment->fresh();
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to accept assignment', [
                'error' => $e->getMessage(),
                'assignment_id' => $assignment->id,
            ]);
            throw $e;
        }
    }

    /**
     * Complete an assignment.
     */
    public function complete(Assignment $assignment, array $data = []): Assignment
    {
        DB::beginTransaction();
        
        try {
            $assignment->complete($data['completion_notes'] ?? null);

            // Update programme statistics
            $assignment->programme->updateStatistics();

            DB::commit();

            // Send completion notification
            $this->sendCompletionNotification($assignment);

            Log::info('Assignment completed', [
                'assignment_id' => $assignment->id,
                'programme_id' => $assignment->programme_id,
            ]);

            return $assignment->fresh();
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to complete assignment', [
                'error' => $e->getMessage(),
                'assignment_id' => $assignment->id,
            ]);
            throw $e;
        }
    }

    /**
     * Cancel an assignment.
     */
    public function cancel(Assignment $assignment, string $reason): Assignment
    {
        DB::beginTransaction();
        
        try {
            $assignment->cancel($reason);

            // Update programme statistics
            $assignment->programme->updateStatistics();

            DB::commit();

            // Send cancellation notification
            $this->sendCancellationNotification($assignment, $reason);

            Log::info('Assignment cancelled', [
                'assignment_id' => $assignment->id,
                'reason' => $reason,
            ]);

            return $assignment->fresh();
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to cancel assignment', [
                'error' => $e->getMessage(),
                'assignment_id' => $assignment->id,
            ]);
            throw $e;
        }
    }

    /**
     * Reassign mentee to a different mentor.
     */
    public function reassign(Assignment $assignment, int $newMentorId, string $reason): Assignment
    {
        DB::beginTransaction();
        
        try {
            // Create new assignment
            $newAssignment = Assignment::create([
                'programme_id' => $assignment->programme_id,
                'mentor_id' => $newMentorId,
                'mentee_id' => $assignment->mentee_id,
                'mentee_application_id' => $assignment->mentee_application_id,
                'previous_assignment_id' => $assignment->id,
                'reassignment_reason' => $reason,
                'goals' => $assignment->goals,
                'milestones' => $assignment->milestones,
                'status' => Assignment::STATUS_PENDING_ACCEPTANCE,
                'assigned_by' => auth()->id(),
                'assigned_at' => now(),
            ]);

            // Mark old assignment as reassigned
            $assignment->update([
                'status' => Assignment::STATUS_REASSIGNED,
                'notes' => ($assignment->notes ?? '') . "\n\nReassigned: " . $reason,
            ]);

            DB::commit();

            // Send notifications
            $this->sendReassignmentNotification($newAssignment, $assignment);

            Log::info('Assignment reassigned', [
                'old_assignment_id' => $assignment->id,
                'new_assignment_id' => $newAssignment->id,
                'new_mentor_id' => $newMentorId,
                'reason' => $reason,
            ]);

            return $newAssignment;
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to reassign assignment', [
                'error' => $e->getMessage(),
                'assignment_id' => $assignment->id,
            ]);
            throw $e;
        }
    }

    /**
     * Get assignments with filtering.
     */
    public function getAssignments(array $filters = [], int $perPage = 15)
    {
        $query = Assignment::with(['programme', 'mentor', 'mentee', 'assigner']);

        // Filter by status
        if (!empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        // Filter by programme
        if (!empty($filters['programme_id'])) {
            $query->where('programme_id', $filters['programme_id']);
        }

        // Filter by mentor
        if (!empty($filters['mentor_id'])) {
            $query->where('mentor_id', $filters['mentor_id']);
        }

        // Filter by mentee
        if (!empty($filters['mentee_id'])) {
            $query->where('mentee_id', $filters['mentee_id']);
        }

        // Filter by user (mentor or mentee)
        if (!empty($filters['user_id'])) {
            $query->where(function ($q) use ($filters) {
                $q->where('mentor_id', $filters['user_id'])
                    ->orWhere('mentee_id', $filters['user_id']);
            });
        }

        // Search
        if (!empty($filters['search'])) {
            $query->where(function ($q) use ($filters) {
                $q->whereHas('mentor', function ($q) use ($filters) {
                    $q->where('name', 'like', '%' . $filters['search'] . '%');
                })->orWhereHas('mentee', function ($q) use ($filters) {
                    $q->where('name', 'like', '%' . $filters['search'] . '%');
                });
            });
        }

        // Sort
        $sortBy = $filters['sort_by'] ?? 'created_at';
        $sortOrder = $filters['sort_order'] ?? 'desc';
        $query->orderBy($sortBy, $sortOrder);

        return $query->paginate($perPage);
    }

    /**
     * Generate milestones from programme.
     */
    protected function generateMilestones(Programme $programme): array
    {
        $milestones = [];
        $sessionsCount = $programme->sessions_count ?: 4;

        for ($i = 1; $i <= $sessionsCount; $i++) {
            $milestones[] = [
                'title' => "Session {$i}",
                'description' => "Complete session {$i} of the mentorship programme",
                'due_date' => null,
                'completed' => false,
            ];
        }

        // Add final milestone
        $milestones[] = [
            'title' => 'Programme Completion',
            'description' => 'Complete all programme requirements and receive certificate',
            'due_date' => null,
            'completed' => false,
        ];

        return $milestones;
    }

    /**
     * Send assignment notification.
     */
    protected function sendAssignmentNotification(Assignment $assignment): void
    {
        try {
            // Notify mentor
            if ($this->emailService->isReady()) {
                $this->emailService->sendTemplate(
                    $assignment->mentor->email,
                    'New Mentee Assignment - ' . $assignment->programme->title,
                    'emails.mentorship.mentor-assignment',
                    [
                        'mentorName' => $assignment->mentor->name,
                        'menteeName' => $assignment->mentee->name,
                        'programmeName' => $assignment->programme->title,
                        'acceptUrl' => route('mentorship.assignments.accept', $assignment),
                        'dashboardUrl' => route('dashboard'),
                    ]
                );
            }

            // Notify mentee
            if ($this->emailService->isReady()) {
                $this->emailService->sendTemplate(
                    $assignment->mentee->email,
                    'Mentor Assignment - ' . $assignment->programme->title,
                    'emails.mentorship.mentee-assignment',
                    [
                        'menteeName' => $assignment->mentee->name,
                        'mentorName' => $assignment->mentor->name,
                        'programmeName' => $assignment->programme->title,
                        'acceptUrl' => route('mentorship.assignments.accept', $assignment),
                        'dashboardUrl' => route('dashboard'),
                    ]
                );
            }

            // Send SMS notifications
            if ($this->smsService->isAvailable()) {
                if ($assignment->mentor->phone) {
                    $this->smsService->sendTemplate(
                        $assignment->mentor->phone,
                        'You have been assigned a new mentee ({mentee}) for {programme}. Please check your dashboard.',
                        [
                            'mentee' => $assignment->mentee->name,
                            'programme' => $assignment->programme->title
                        ]
                    );
                }

                if ($assignment->mentee->phone) {
                    $this->smsService->sendTemplate(
                        $assignment->mentee->phone,
                        'You have been assigned a mentor ({mentor}) for {programme}. Please check your dashboard.',
                        [
                            'mentor' => $assignment->mentor->name,
                            'programme' => $assignment->programme->title
                        ]
                    );
                }
            }
        } catch (\Exception $e) {
            Log::error('Failed to send assignment notification', [
                'assignment_id' => $assignment->id,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Send completion notification.
     */
    protected function sendCompletionNotification(Assignment $assignment): void
    {
        // Notify mentee about completion and certificate eligibility
        try {
            if ($this->emailService->isReady()) {
                $this->emailService->sendTemplate(
                    $assignment->mentee->email,
                    'Mentorship Programme Completed - ' . $assignment->programme->title,
                    'emails.mentorship.programme-completed',
                    [
                        'menteeName' => $assignment->mentee->name,
                        'mentorName' => $assignment->mentor->name,
                        'programmeName' => $assignment->programme->title,
                        'certificateEligible' => $assignment->certificate_eligible,
                        'dashboardUrl' => route('dashboard'),
                    ]
                );
            }
        } catch (\Exception $e) {
            Log::error('Failed to send completion notification', [
                'assignment_id' => $assignment->id,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Send cancellation notification.
     */
    protected function sendCancellationNotification(Assignment $assignment, string $reason): void
    {
        // Notify both parties about cancellation
        try {
            if ($this->emailService->isReady()) {
                $this->emailService->sendTemplate(
                    $assignment->mentor->email,
                    'Assignment Cancelled - ' . $assignment->programme->title,
                    'emails.mentorship.assignment-cancelled',
                    [
                        'userName' => $assignment->mentor->name,
                        'otherPartyName' => $assignment->mentee->name,
                        'programmeName' => $assignment->programme->title,
                        'reason' => $reason,
                    ]
                );

                $this->emailService->sendTemplate(
                    $assignment->mentee->email,
                    'Assignment Cancelled - ' . $assignment->programme->title,
                    'emails.mentorship.assignment-cancelled',
                    [
                        'userName' => $assignment->mentee->name,
                        'otherPartyName' => $assignment->mentor->name,
                        'programmeName' => $assignment->programme->title,
                        'reason' => $reason,
                    ]
                );
            }
        } catch (\Exception $e) {
            Log::error('Failed to send cancellation notification', [
                'assignment_id' => $assignment->id,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Send reassignment notification.
     */
    protected function sendReassignmentNotification(Assignment $newAssignment, Assignment $oldAssignment): void
    {
        // Notify new mentor and mentee about reassignment
        try {
            if ($this->emailService->isReady()) {
                $this->emailService->sendTemplate(
                    $newAssignment->mentor->email,
                    'New Mentee Assignment - ' . $newAssignment->programme->title,
                    'emails.mentorship.mentor-assignment',
                    [
                        'mentorName' => $newAssignment->mentor->name,
                        'menteeName' => $newAssignment->mentee->name,
                        'programmeName' => $newAssignment->programme->title,
                        'acceptUrl' => route('mentorship.assignments.accept', $newAssignment),
                        'dashboardUrl' => route('dashboard'),
                    ]
                );

                $this->emailService->sendTemplate(
                    $newAssignment->mentee->email,
                    'Mentor Reassigned - ' . $newAssignment->programme->title,
                    'emails.mentorship.mentor-reassigned',
                    [
                        'menteeName' => $newAssignment->mentee->name,
                        'newMentorName' => $newAssignment->mentor->name,
                        'programmeName' => $newAssignment->programme->title,
                        'reason' => $newAssignment->reassignment_reason,
                        'dashboardUrl' => route('dashboard'),
                    ]
                );
            }
        } catch (\Exception $e) {
            Log::error('Failed to send reassignment notification', [
                'assignment_id' => $newAssignment->id,
                'error' => $e->getMessage(),
            ]);
        }
    }
}
