<?php

namespace Modules\Results\Services;

use Modules\Results\Models\Result;
use Modules\Students\Models\Student;
use Modules\Academic\Models\Subject;
use Modules\Academic\Models\Classroom;
use Modules\Academic\Models\AcademicYear;
use Modules\Academic\Models\ExamType;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\UploadedFile;

class ResultCsvService
{
    private ResultCalculationService $calculationService;

    public function __construct(ResultCalculationService $calculationService)
    {
        $this->calculationService = $calculationService;
    }

    /**
     * Import results from CSV file
     */
    public function importResults(UploadedFile $file, int $academicYearId, int $examTypeId, int $classroomId, int $teacherId): array
    {
        $results = [];
        $errors = [];
        $successCount = 0;
        $errorCount = 0;

        // Validate file
        if (!$this->validateCsvFile($file)) {
            throw new \Exception('Invalid CSV file format');
        }

        // Read CSV content
        $csvData = $this->readCsvFile($file);
        
        // Validate headers
        $expectedHeaders = ['student_id', 'subject_code', 'marks'];
        if (!$this->validateHeaders($csvData[0] ?? [], $expectedHeaders)) {
            throw new \Exception('Invalid CSV headers. Expected: ' . implode(', ', $expectedHeaders));
        }

        // Remove header row
        array_shift($csvData);

        DB::transaction(function () use ($csvData, $academicYearId, $examTypeId, $classroomId, $teacherId, &$results, &$errors, &$successCount, &$errorCount) {
            foreach ($csvData as $rowIndex => $row) {
                $rowNumber = $rowIndex + 2; // +2 because we removed header and arrays are 0-indexed
                
                try {
                    $this->processResultRow($row, $academicYearId, $examTypeId, $classroomId, $teacherId, $rowNumber);
                    $successCount++;
                    $results[] = [
                        'row' => $rowNumber,
                        'status' => 'success',
                        'data' => $row,
                    ];
                } catch (\Exception $e) {
                    $errorCount++;
                    $errors[] = [
                        'row' => $rowNumber,
                        'error' => $e->getMessage(),
                        'data' => $row,
                    ];
                }
            }
        });

        // Recalculate summaries for the classroom
        if ($successCount > 0) {
            try {
                $this->calculationService->calculateClassroomSummaries($classroomId, $academicYearId, $examTypeId);
            } catch (\Exception $e) {
                \Log::warning("Failed to recalculate classroom summaries: " . $e->getMessage());
            }
        }

        return [
            'total_rows' => count($csvData),
            'success_count' => $successCount,
            'error_count' => $errorCount,
            'results' => $results,
            'errors' => $errors,
        ];
    }

    /**
     * Process a single result row
     */
    private function processResultRow(array $row, int $academicYearId, int $examTypeId, int $classroomId, int $teacherId, int $rowNumber): void
    {
        // Validate row data
        $validator = Validator::make([
            'student_id' => $row[0] ?? null,
            'subject_code' => $row[1] ?? null,
            'marks' => $row[2] ?? null,
        ], [
            'student_id' => 'required|string',
            'subject_code' => 'required|string',
            'marks' => 'required|integer|min:0|max:100',
        ]);

        if ($validator->fails()) {
            throw new \Exception("Row {$rowNumber}: " . implode(', ', $validator->errors()->all()));
        }

        $studentId = trim($row[0]);
        $subjectCode = trim($row[1]);
        $marks = (int) trim($row[2]);

        // Find student
        $student = Student::where('student_id', $studentId)->first();
        if (!$student) {
            throw new \Exception("Row {$rowNumber}: Student with ID '{$studentId}' not found");
        }

        // Find subject
        $subject = Subject::where('code', $subjectCode)->where('is_active', true)->first();
        if (!$subject) {
            throw new \Exception("Row {$rowNumber}: Subject with code '{$subjectCode}' not found");
        }

        // Verify student is enrolled in the classroom
        $enrollment = \Modules\Enrollment\Models\Enrollment::where('student_id', $student->id)
            ->where('classroom_id', $classroomId)
            ->where('academic_year_id', $academicYearId)
            ->where('is_active', true)
            ->first();

        if (!$enrollment) {
            throw new \Exception("Row {$rowNumber}: Student '{$studentId}' is not enrolled in this classroom");
        }

        // Create or update result
        Result::updateOrCreate(
            [
                'student_id' => $student->id,
                'subject_id' => $subject->id,
                'classroom_id' => $classroomId,
                'academic_year_id' => $academicYearId,
                'exam_type_id' => $examTypeId,
            ],
            [
                'marks' => $marks,
                'teacher_id' => $teacherId,
                'entered_at' => now(),
            ]
        );
    }

    /**
     * Export results to CSV
     */
    public function exportResults(int $classroomId, int $academicYearId, int $examTypeId): string
    {
        $classroom = Classroom::with(['academicYear', 'subjects'])->findOrFail($classroomId);
        $examType = ExamType::findOrFail($examTypeId);

        // Get all students in the classroom
        $enrollments = \Modules\Enrollment\Models\Enrollment::forClassroom($classroomId)
            ->forAcademicYear($academicYearId)
            ->active()
            ->with(['student', 'student.user'])
            ->get();

        // Get all subjects for the classroom
        $subjects = $classroom->subjects()->where('is_active', true)->get();

        // Prepare CSV data
        $csvData = [];
        
        // Headers
        $headers = ['Student ID', 'Student Name'];
        foreach ($subjects as $subject) {
            $headers[] = $subject->name . ' (' . $subject->code . ')';
        }
        $headers = array_merge($headers, ['Total Marks', 'Average', 'Grade', 'Division', 'Rank']);
        $csvData[] = $headers;

        // Data rows
        foreach ($enrollments as $enrollment) {
            $student = $enrollment->student;
            $row = [$student->student_id, $student->full_name];

            // Get results for each subject
            $totalMarks = 0;
            $subjectCount = 0;
            foreach ($subjects as $subject) {
                $result = Result::forStudent($student->id)
                    ->forAcademicYear($academicYearId)
                    ->forExamType($examTypeId)
                    ->forSubject($subject->id)
                    ->first();

                if ($result) {
                    $row[] = $result->marks;
                    $totalMarks += $result->marks;
                    $subjectCount++;
                } else {
                    $row[] = '-';
                }
            }

            // Add summary data
            $summary = \Modules\Results\Models\StudentResultSummary::forStudent($student->id)
                ->forAcademicYear($academicYearId)
                ->forExamType($examTypeId)
                ->first();

            if ($summary) {
                $row[] = $summary->total_marks;
                $row[] = $summary->average_marks;
                $row[] = $this->getOverallGrade($summary->average_marks);
                $row[] = $summary->division ?? '-';
                $row[] = $summary->rank;
            } else {
                $row = array_merge($row, ['-', '-', '-', '-', '-']);
            }

            $csvData[] = $row;
        }

        return $this->generateCsvContent($csvData);
    }

    /**
     * Generate CSV template for import
     */
    public function generateImportTemplate(int $classroomId, int $academicYearId): string
    {
        $classroom = Classroom::with(['subjects'])->findOrFail($classroomId);
        
        // Get enrolled students
        $enrollments = \Modules\Enrollment\Models\Enrollment::forClassroom($classroomId)
            ->forAcademicYear($academicYearId)
            ->active()
            ->with(['student'])
            ->get();

        // Get subjects
        $subjects = $classroom->subjects()->where('is_active', true)->get();

        $csvData = [];
        $csvData[] = ['student_id', 'subject_code', 'marks']; // Headers

        // Generate sample rows for each student-subject combination
        foreach ($enrollments as $enrollment) {
            foreach ($subjects as $subject) {
                $csvData[] = [
                    $enrollment->student->student_id,
                    $subject->code,
                    '' // Empty marks field for user to fill
                ];
            }
        }

        return $this->generateCsvContent($csvData);
    }

    /**
     * Validate CSV file
     */
    private function validateCsvFile(UploadedFile $file): bool
    {
        return $file->isValid() && 
               in_array($file->getClientOriginalExtension(), ['csv', 'txt']) &&
               $file->getSize() <= 5 * 1024 * 1024; // 5MB max
    }

    /**
     * Read CSV file content
     */
    private function readCsvFile(UploadedFile $file): array
    {
        $content = file_get_contents($file->getPathname());
        $lines = explode("\n", $content);
        $data = [];

        foreach ($lines as $line) {
            $line = trim($line);
            if (!empty($line)) {
                $data[] = str_getcsv($line);
            }
        }

        return $data;
    }

    /**
     * Validate CSV headers
     */
    private function validateHeaders(array $headers, array $expectedHeaders): bool
    {
        $headers = array_map('trim', $headers);
        return count(array_intersect($headers, $expectedHeaders)) === count($expectedHeaders);
    }

    /**
     * Generate CSV content from array
     */
    private function generateCsvContent(array $data): string
    {
        $output = fopen('php://temp', 'r+');
        
        foreach ($data as $row) {
            fputcsv($output, $row);
        }
        
        rewind($output);
        $content = stream_get_contents($output);
        fclose($output);
        
        return $content;
    }

    /**
     * Get overall grade based on average marks
     */
    private function getOverallGrade(float $averageMarks): string
    {
        if ($averageMarks >= 80) return 'A';
        if ($averageMarks >= 70) return 'B';
        if ($averageMarks >= 60) return 'C';
        if ($averageMarks >= 50) return 'D';
        return 'F';
    }
}
