<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\FileStore;

class EnrollmentService
{
    private FileStore $enrollments;
    private FileStore $progress;
    private CourseService $courses;
    private CourseContentService $content;
    private ActivityLogService $logs;

    public function __construct()
    {
        $this->enrollments = new FileStore(storage_path('data/enrollments.json'));
        $this->progress = new FileStore(storage_path('data/lesson_progress.json'));
        $this->courses = new CourseService();
        $this->content = new CourseContentService();
        $this->logs = new ActivityLogService();
    }

    public function enrollFree(int $courseId, array $user): array
    {
        if (($user['role'] ?? '') !== 'student') {
            return ['ok' => false, 'error' => 'Apenas alunos podem inscrever-se em cursos.'];
        }

        $course = $this->courses->find($courseId);

        if (!$course || ($course['status'] ?? '') !== 'published') {
            return ['ok' => false, 'error' => 'Curso indisponivel.'];
        }

        if (empty($course['is_free'])) {
            return ['ok' => false, 'error' => 'Este curso e pago. O acesso sera liberado pelo modulo de pagamentos.'];
        }

        $existing = $this->findEnrollment((int) $user['id'], $courseId);

        if ($existing) {
            return ['ok' => true, 'enrollment' => $existing];
        }

        $expiresAt = null;

        if (($course['access_type'] ?? 'lifetime') === 'limited') {
            $days = max(1, (int) ($course['access_duration_days'] ?? 1));
            $expiresAt = date('c', strtotime("+{$days} days"));
        }

        $enrollment = $this->enrollments->insert([
            'student_id' => (int) $user['id'],
            'course_id' => $courseId,
            'order_id' => null,
            'payment_id' => null,
            'access_type' => $course['access_type'] ?? 'lifetime',
            'starts_at' => date('c'),
            'expires_at' => $expiresAt,
            'status' => 'active',
            'last_lesson_id' => null,
            'created_at' => date('c'),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('course_enrolled', "Aluno inscrito no curso {$course['title']}.", (int) $user['id']);

        return ['ok' => true, 'enrollment' => $enrollment];
    }

    public function enrollPaid(int $courseId, int $studentId, ?int $orderId, ?int $paymentId): array
    {
        $course = $this->courses->find($courseId);

        if (!$course || ($course['status'] ?? '') !== 'published') {
            return ['ok' => false, 'error' => 'Curso indisponivel.'];
        }

        $existing = $this->findEnrollment($studentId, $courseId);

        if ($existing) {
            return ['ok' => true, 'enrollment' => $existing];
        }

        $expiresAt = null;

        if (($course['access_type'] ?? 'lifetime') === 'limited') {
            $days = max(1, (int) ($course['access_duration_days'] ?? 1));
            $expiresAt = date('c', strtotime("+{$days} days"));
        }

        $enrollment = $this->enrollments->insert([
            'student_id' => $studentId,
            'course_id' => $courseId,
            'order_id' => $orderId,
            'payment_id' => $paymentId,
            'access_type' => $course['access_type'] ?? 'lifetime',
            'starts_at' => date('c'),
            'expires_at' => $expiresAt,
            'status' => 'active',
            'last_lesson_id' => null,
            'created_at' => date('c'),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('paid_course_enrolled', "Acesso pago liberado para curso {$course['title']}.", $studentId);

        return ['ok' => true, 'enrollment' => $enrollment];
    }

    public function enrolledCourses(int $studentId): array
    {
        $items = [];

        foreach ($this->enrollments->all() as $enrollment) {
            if ((int) ($enrollment['student_id'] ?? 0) !== $studentId) {
                continue;
            }

            $course = $this->courses->find((int) $enrollment['course_id']);

            if (!$course) {
                continue;
            }

            $items[] = [
                'enrollment' => $enrollment,
                'course' => $course,
                'progress' => $this->courseProgress((int) $enrollment['id']),
            ];
        }

        usort($items, fn (array $a, array $b): int => strcmp($b['enrollment']['updated_at'] ?? '', $a['enrollment']['updated_at'] ?? ''));

        return $items;
    }

    public function findEnrollment(int $studentId, int $courseId): ?array
    {
        foreach ($this->enrollments->all() as $enrollment) {
            if ((int) ($enrollment['student_id'] ?? 0) === $studentId && (int) ($enrollment['course_id'] ?? 0) === $courseId) {
                return $this->isEnrollmentActive($enrollment) ? $enrollment : null;
            }
        }

        return null;
    }

    public function enrollmentById(int $id): ?array
    {
        foreach ($this->enrollments->all() as $enrollment) {
            if ((int) ($enrollment['id'] ?? 0) === $id) {
                return $this->isEnrollmentActive($enrollment) ? $enrollment : null;
            }
        }

        return null;
    }

    public function hasAccess(int $studentId, int $courseId): bool
    {
        return $this->findEnrollment($studentId, $courseId) !== null;
    }

    public function firstLessonId(int $courseId): ?int
    {
        foreach ($this->content->courseContent($courseId) as $section) {
            foreach ($section['lessons'] as $lesson) {
                return (int) $lesson['id'];
            }
        }

        return null;
    }

    public function nextLessonId(int $courseId, int $currentLessonId): ?int
    {
        $found = false;

        foreach ($this->content->courseContent($courseId) as $section) {
            foreach ($section['lessons'] as $lesson) {
                if ($found) {
                    return (int) $lesson['id'];
                }

                if ((int) $lesson['id'] === $currentLessonId) {
                    $found = true;
                }
            }
        }

        return null;
    }

    public function previousLessonId(int $courseId, int $currentLessonId): ?int
    {
        $previous = null;

        foreach ($this->content->courseContent($courseId) as $section) {
            foreach ($section['lessons'] as $lesson) {
                if ((int) $lesson['id'] === $currentLessonId) {
                    return $previous;
                }

                $previous = (int) $lesson['id'];
            }
        }

        return null;
    }

    public function lessonProgress(int $enrollmentId, int $lessonId): array
    {
        foreach ($this->progress->all() as $progress) {
            if ((int) ($progress['enrollment_id'] ?? 0) === $enrollmentId && (int) ($progress['lesson_id'] ?? 0) === $lessonId) {
                return $progress;
            }
        }

        return [
            'enrollment_id' => $enrollmentId,
            'lesson_id' => $lessonId,
            'is_completed' => false,
            'last_watched_second' => 0,
        ];
    }

    public function completedLessonIds(int $enrollmentId): array
    {
        $ids = [];

        foreach ($this->progress->all() as $progress) {
            if ((int) ($progress['enrollment_id'] ?? 0) === $enrollmentId && !empty($progress['is_completed'])) {
                $ids[] = (int) $progress['lesson_id'];
            }
        }

        return $ids;
    }

    public function lessonCompleted(int $enrollmentId, int $lessonId): bool
    {
        foreach ($this->progress->all() as $progress) {
            if ((int) ($progress['enrollment_id'] ?? 0) === $enrollmentId && (int) ($progress['lesson_id'] ?? 0) === $lessonId) {
                return !empty($progress['is_completed']);
            }
        }

        return false;
    }

    public function markCompleted(int $enrollmentId, int $lessonId, int $studentId): array
    {
        $enrollment = $this->enrollmentById($enrollmentId);
        $lesson = $this->content->lesson($lessonId);

        if (!$enrollment || !$lesson || (int) $enrollment['student_id'] !== $studentId || (int) $lesson['course_id'] !== (int) $enrollment['course_id']) {
            return ['ok' => false, 'error' => 'Aula ou matricula invalida.'];
        }

        $records = $this->progress->all();
        $updated = false;

        foreach ($records as $index => $record) {
            if ((int) ($record['enrollment_id'] ?? 0) === $enrollmentId && (int) ($record['lesson_id'] ?? 0) === $lessonId) {
                $records[$index]['is_completed'] = true;
                $records[$index]['completed_at'] = date('c');
                $records[$index]['updated_at'] = date('c');
                $updated = true;
                break;
            }
        }

        if (!$updated) {
            $records[] = [
                'id' => $this->nextProgressId($records),
                'enrollment_id' => $enrollmentId,
                'student_id' => $studentId,
                'course_id' => (int) $enrollment['course_id'],
                'lesson_id' => $lessonId,
                'is_completed' => true,
                'completed_at' => date('c'),
                'last_watched_second' => 0,
                'created_at' => date('c'),
                'updated_at' => date('c'),
            ];
        }

        $this->progress->save($records);
        $this->touchEnrollment($enrollmentId, ['last_lesson_id' => $lessonId]);

        return ['ok' => true, 'progress' => $this->courseProgress($enrollmentId)];
    }

    public function saveWatchPosition(int $enrollmentId, int $lessonId, int $studentId, int $second): array
    {
        $enrollment = $this->enrollmentById($enrollmentId);
        $lesson = $this->content->lesson($lessonId);

        if (!$enrollment || !$lesson || (int) $enrollment['student_id'] !== $studentId || (int) $lesson['course_id'] !== (int) $enrollment['course_id']) {
            return ['ok' => false, 'error' => 'Aula ou matricula invalida.'];
        }

        $records = $this->progress->all();
        $updated = false;
        $second = max(0, $second);

        foreach ($records as $index => $record) {
            if ((int) ($record['enrollment_id'] ?? 0) === $enrollmentId && (int) ($record['lesson_id'] ?? 0) === $lessonId) {
                $records[$index]['last_watched_second'] = max((int) ($record['last_watched_second'] ?? 0), $second);
                $records[$index]['updated_at'] = date('c');
                $updated = true;
                break;
            }
        }

        if (!$updated) {
            $records[] = [
                'id' => $this->nextProgressId($records),
                'enrollment_id' => $enrollmentId,
                'student_id' => $studentId,
                'course_id' => (int) $enrollment['course_id'],
                'lesson_id' => $lessonId,
                'is_completed' => false,
                'completed_at' => null,
                'last_watched_second' => $second,
                'created_at' => date('c'),
                'updated_at' => date('c'),
            ];
        }

        $this->progress->save($records);
        $this->touchEnrollment($enrollmentId, ['last_lesson_id' => $lessonId]);

        return ['ok' => true];
    }

    public function touchEnrollment(int $enrollmentId, array $changes): void
    {
        $records = $this->enrollments->all();

        foreach ($records as $index => $enrollment) {
            if ((int) ($enrollment['id'] ?? 0) === $enrollmentId) {
                $records[$index] = array_merge($enrollment, $changes, ['updated_at' => date('c')]);
                break;
            }
        }

        $this->enrollments->save($records);
    }

    public function courseProgress(int $enrollmentId): array
    {
        $enrollment = $this->enrollmentById($enrollmentId);

        if (!$enrollment) {
            return ['completed' => 0, 'total' => 0, 'percentage' => 0];
        }

        $lessonIds = [];

        foreach ($this->content->courseContent((int) $enrollment['course_id']) as $section) {
            foreach ($section['lessons'] as $lesson) {
                $lessonIds[] = (int) $lesson['id'];
            }
        }

        $completed = 0;

        foreach ($lessonIds as $lessonId) {
            if ($this->lessonCompleted($enrollmentId, $lessonId)) {
                $completed++;
            }
        }

        $total = count($lessonIds);

        return [
            'completed' => $completed,
            'total' => $total,
            'percentage' => $total > 0 ? (int) round(($completed / $total) * 100) : 0,
        ];
    }

    private function isEnrollmentActive(array $enrollment): bool
    {
        if (($enrollment['status'] ?? '') !== 'active') {
            return false;
        }

        return empty($enrollment['expires_at']) || strtotime((string) $enrollment['expires_at']) >= time();
    }

    private function nextProgressId(array $records): int
    {
        $ids = array_column($records, 'id');

        return $ids ? max($ids) + 1 : 1;
    }
}
