<?php

namespace App\Services;

use App\Models\User;
use App\Models\Batch;
use App\Models\SchoolClass;
use App\Models\SchoolSession;
use App\Models\Result;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class PromotionService
{
    public function promoteStudentsByClass(int $fromClassId, int $toClassId, bool $onlyEligible = false): array
    {
        $fromClass = SchoolClass::findOrFail($fromClassId);
        $toClass = SchoolClass::findOrFail($toClassId);
        
        $batches = $fromClass->batches()->where('is_active', true)->get();
        $promoted = [];
        $failed = [];
        $warnings = [];
        
        if ($this->isClassDemotion($fromClass, $toClass)) {
            $warnings[] = "Warning: Moving students from {$fromClass->display_name} to {$toClass->display_name} is a demotion to a lower class.";
        }
        
        foreach ($batches as $batch) {
            $students = $batch->students;
            
            if ($onlyEligible) {
                $students = $this->filterEligibleStudents($students);
            }
            
            if ($students->isNotEmpty()) {
                $newBatch = $this->createNewBatch($batch, $toClass);
                
                foreach ($students as $student) {
                    try {
                        $result = $this->assignStudentToBatch($student, $newBatch);
                        $promoted[] = $student;
                        if (isset($result['warning'])) {
                            $warnings[] = $result['warning'];
                        }
                    } catch (\Exception $e) {
                        $failed[] = ['student' => $student, 'error' => $e->getMessage()];
                    }
                }
            }
        }
        
        return [
            'promoted' => $promoted,
            'failed' => $failed,
            'warnings' => $warnings,
            'total_students' => collect($promoted)->count() + count($failed),
            'promoted_count' => count($promoted),
            'failed_count' => count($failed)
        ];
    }
    
    public function promoteStudentsByBatch(int $batchId, int $toClassId, bool $onlyEligible = false, int $toBatchId = null): array
    {
        $batch = Batch::findOrFail($batchId);
        $toClass = SchoolClass::findOrFail($toClassId);
        
        $students = $batch->students()->get();
        
        if ($onlyEligible) {
            $students = $this->filterEligibleStudents($students);
        }
        
        $promoted = [];
        $failed = [];
        $warnings = [];
        
        if ($this->isClassDemotion($batch->schoolClass, $toClass)) {
            $warnings[] = "Warning: Moving students from {$batch->schoolClass->display_name} to {$toClass->display_name} is a demotion to a lower class.";
        }
        
        if ($students->isNotEmpty()) {
            if ($toBatchId) {
                $targetBatch = Batch::findOrFail($toBatchId);
            } else {
                $targetBatch = $this->createNewBatch($batch, $toClass);
            }
            
            foreach ($students as $student) {
                try {
                    $result = $this->assignStudentToBatch($student, $targetBatch);
                    $promoted[] = $student;
                    if (isset($result['warning'])) {
                        $warnings[] = $result['warning'];
                    }
                } catch (\Exception $e) {
                    $failed[] = ['student' => $student, 'error' => $e->getMessage()];
                }
            }
        }
        
        return [
            'promoted' => $promoted,
            'failed' => $failed,
            'warnings' => $warnings,
            'total_students' => $students->count(),
            'promoted_count' => count($promoted),
            'failed_count' => count($failed)
        ];
    }
    
    public function promoteIndividualStudent(int $studentId, int $toClassId, int $toBatchId = null): array
    {
        $student = User::findOrFail($studentId);
        $toClass = SchoolClass::findOrFail($toClassId);
        $fromClass = $student->batch->schoolClass;
        
        $warnings = [];
        
        if ($this->isClassDemotion($fromClass, $toClass)) {
            $warnings[] = "Warning: Moving student from {$fromClass->display_name} to {$toClass->display_name} is a demotion to a lower class.";
        }
        
        if ($toBatchId) {
            $toBatch = Batch::findOrFail($toBatchId);
            $result = $this->assignStudentToBatch($student, $toBatch, false);
        } else {
            $newBatch = $this->createNewBatch($student->batch, $toClass);
            $result = $this->assignStudentToBatch($student, $newBatch, false);
        }
        
        if (isset($result['warning'])) {
            $warnings[] = $result['warning'];
        }
        
        return [
            'success' => $result['success'],
            'warnings' => $warnings
        ];
    }
    
    private function assignStudentToBatch(User $student, Batch $targetBatch, bool $renameBatch = true): array
    {
        DB::beginTransaction();
        
        try {
            $warning = null;
            
            // Check if batch will exceed capacity
            if ($targetBatch->current_students >= $targetBatch->capacity) {
                $newCount = $targetBatch->current_students + 1;
                $warning = "Batch '{$targetBatch->name}' is now over capacity ({$newCount}/{$targetBatch->capacity})";
            }
            
            $oldBatch = $student->batch;
            $student->update(['batch_id' => $targetBatch->id]);
            
            if ($oldBatch) {
                $oldBatch->decrement('current_students');
                
                $refreshedBatch = $oldBatch->fresh();
                $actualStudentCount = $refreshedBatch->students()->count();
                
                if ($actualStudentCount <= 0) {
                    // Delete batch if empty
                    $refreshedBatch->delete();
                } else if ($renameBatch) {
                    // Update current_students to match actual count and rename batch
                    $refreshedBatch->update(['current_students' => $actualStudentCount]);
                    $this->renameBatchForRepeaters($refreshedBatch);
                }
            }
            $targetBatch->increment('current_students');
            
            DB::commit();
            return ['success' => true, 'warning' => $warning];
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }
    
    private function createNewBatch(Batch $sourceBatch, SchoolClass $toClass): Batch
    {
        $sourceClassName = $sourceBatch->schoolClass->display_name;
        $newClassName = $toClass->display_name;
        $batchName = "{$newClassName} (from batch {$sourceBatch->name}, {$sourceClassName})";
        
        // Check if batch with this name already exists in the destination class
        $existingBatch = Batch::where('name', $batchName)
            ->where('school_class_id', $toClass->id)
            ->where('is_active', true)
            ->where('graduated', false)
            ->first();
            
        if ($existingBatch) {
            return $existingBatch;
        }
        
        return Batch::create([
            'name' => $batchName,
            'school_class_id' => $toClass->id,
            'description' => "Promoted from {$sourceBatch->name} in {$sourceClassName}",
            'is_active' => true,
            'graduated' => false,
            'capacity' => $sourceBatch->capacity,
            'current_students' => 0
        ]);
    }
    
    private function getStudentsInClass(int $classId): Collection
    {
        return User::whereHas('batch', function ($query) use ($classId) {
            $query->where('school_class_id', $classId);
        })->get();
    }
    
    public function filterEligibleStudents(Collection $students): Collection
    {
        $activeSession = SchoolSession::where('is_active', true)->first();
        
        if (!$activeSession) {
            return collect();
        }
        
        $passPercentage = $activeSession->overall_pass_percentage ?? 50;
        
        return $students->filter(function ($student) use ($passPercentage) {
            $annualAverage = $this->calculateStudentAnnualAverage($student);
            return $annualAverage >= $passPercentage;
        });
    }
    
    private function calculateStudentAnnualAverage(User $student): float
    {
        $currentYear = now()->year;
        $nextYear = $currentYear + 1;
        
        $sessions = SchoolSession::whereBetween('created_at', [
            "{$currentYear}-01-01",
            "{$nextYear}-12-31"
        ])->pluck('id');
        
        if ($sessions->isEmpty()) {
            return 0;
        }
        
        $approvedSessions = \App\Models\ResultApproval::where('student_id', $student->id)
            ->whereIn('session_id', $sessions)
            ->pluck('session_id');
            
        $results = Result::where('student_id', $student->id)
            ->whereIn('session_id', $approvedSessions)
            ->get();
        
        if ($results->isEmpty()) {
            return 0;
        }
        
        $sessionAverages = [];
        
        foreach ($sessions as $sessionId) {
            $sessionResults = $results->where('session_id', $sessionId);
            
            if ($sessionResults->isNotEmpty()) {
                $totalScore = $sessionResults->sum(function ($result) {
                    return $result->ca_score + $result->exam_score;
                });
                
                $sessionAverages[] = $totalScore / $sessionResults->count();
            }
        }
        
        return empty($sessionAverages) ? 0 : array_sum($sessionAverages) / count($sessionAverages);
    }
    
    private function renameBatchForRepeaters(Batch $batch): void
    {
        // Check if already renamed to avoid duplicate renaming
        if (strpos($batch->name, '(Repeated,') === false) {
            $activeSession = SchoolSession::where('is_active', true)->first();
            $sessionName = $activeSession ? $activeSession->name : now()->year;
            
            $batch->update([
                'name' => "{$batch->name} (Repeated, {$sessionName})"
            ]);
        }
    }
    
    public function getPromotionCandidates(int $classId): array
    {
        $students = $this->getStudentsInClass($classId);
        $eligible = $this->filterEligibleStudents($students);
        $ineligible = $students->diff($eligible);
        
        return [
            'all_students' => $students,
            'eligible_students' => $eligible,
            'ineligible_students' => $ineligible,
            'total_count' => $students->count(),
            'eligible_count' => $eligible->count(),
            'ineligible_count' => $ineligible->count()
        ];
    }
    
    private function isClassDemotion(SchoolClass $fromClass, SchoolClass $toClass): bool
    {
        return $toClass->order < $fromClass->order;
    }
}