<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\FileStore;

class CourseContentService
{
    private FileStore $sections;
    private FileStore $lessons;
    private FileStore $materials;
    private CourseService $courses;
    private ActivityLogService $logs;

    public function __construct()
    {
        $this->sections = new FileStore(storage_path('data/course_sections.json'));
        $this->lessons = new FileStore(storage_path('data/lessons.json'));
        $this->materials = new FileStore(storage_path('data/lesson_materials.json'));
        $this->courses = new CourseService();
        $this->logs = new ActivityLogService();
    }

    public function courseContent(int $courseId): array
    {
        $sections = array_values(array_filter(
            $this->sections->all(),
            fn (array $section): bool => (int) ($section['course_id'] ?? 0) === $courseId
        ));

        usort($sections, fn (array $a, array $b): int => (int) $a['position'] <=> (int) $b['position']);

        foreach ($sections as $index => $section) {
            $lessons = array_values(array_filter(
                $this->lessons->all(),
                fn (array $lesson): bool => (int) ($lesson['section_id'] ?? 0) === (int) $section['id']
            ));

            usort($lessons, fn (array $a, array $b): int => (int) $a['position'] <=> (int) $b['position']);

            foreach ($lessons as $lessonIndex => $lesson) {
                $lessons[$lessonIndex]['materials'] = $this->lessonMaterials((int) $lesson['id']);
            }

            $sections[$index]['lessons'] = $lessons;
        }

        return $sections;
    }

    public function addSection(int $courseId, array $data, array $user): array
    {
        $course = $this->courses->find($courseId);

        if (!$course || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Curso nao encontrado ou sem permissao.'];
        }

        $title = trim($data['title'] ?? '');

        if ($title === '') {
            return ['ok' => false, 'error' => 'O titulo do modulo e obrigatorio.'];
        }

        $section = $this->sections->insert([
            'course_id' => $courseId,
            'title' => $title,
            'description' => trim($data['description'] ?? ''),
            'position' => $this->nextSectionPosition($courseId),
            'status' => 'active',
            'created_at' => date('c'),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('course_section_created', "Modulo {$title} criado.", (int) $user['id']);

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

    public function updateSection(int $courseId, int $sectionId, array $data, array $user): array
    {
        $course = $this->courses->find($courseId);
        $section = $this->section($sectionId);

        if (!$course || !$section || (int) ($section['course_id'] ?? 0) !== $courseId || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Modulo nao encontrado ou sem permissao.'];
        }

        $title = trim($data['title'] ?? '');

        if ($title === '') {
            return ['ok' => false, 'error' => 'O titulo do modulo e obrigatorio.'];
        }

        $updated = $this->updateSectionRecord($sectionId, [
            'title' => $title,
            'description' => trim($data['description'] ?? ''),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('course_section_updated', "Modulo {$title} actualizado.", (int) $user['id']);

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

    public function deleteSection(int $courseId, int $sectionId, array $user): array
    {
        $course = $this->courses->find($courseId);
        $section = $this->section($sectionId);

        if (!$course || !$section || (int) ($section['course_id'] ?? 0) !== $courseId || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Modulo nao encontrado ou sem permissao.'];
        }

        $lessons = array_values(array_filter(
            $this->lessons->all(),
            fn (array $lesson): bool => (int) ($lesson['section_id'] ?? 0) === $sectionId
        ));

        foreach ($lessons as $lesson) {
            $this->deleteLessonRecord((int) $lesson['id']);
        }

        $sections = array_filter(
            $this->sections->all(),
            fn (array $item): bool => (int) ($item['id'] ?? 0) !== $sectionId
        );
        $this->sections->save($sections);
        $this->normalizeSectionPositions($courseId);

        $this->logs->log('course_section_deleted', "Modulo {$section['title']} removido.", (int) $user['id']);

        return ['ok' => true];
    }

    public function moveSection(int $courseId, int $sectionId, string $direction, array $user): array
    {
        $course = $this->courses->find($courseId);
        $section = $this->section($sectionId);

        if (!$course || !$section || (int) ($section['course_id'] ?? 0) !== $courseId || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Modulo nao encontrado ou sem permissao.'];
        }

        $sections = array_values(array_filter(
            $this->sections->all(),
            fn (array $item): bool => (int) ($item['course_id'] ?? 0) === $courseId
        ));
        usort($sections, fn (array $a, array $b): int => (int) $a['position'] <=> (int) $b['position']);
        $index = array_search($sectionId, array_map(fn (array $item): int => (int) $item['id'], $sections), true);
        $target = $direction === 'up' ? $index - 1 : $index + 1;

        if ($index === false || !isset($sections[$target])) {
            return ['ok' => true];
        }

        $currentPosition = (int) $sections[$index]['position'];
        $targetPosition = (int) $sections[$target]['position'];
        $records = $this->sections->all();

        foreach ($records as $recordIndex => $record) {
            if ((int) ($record['id'] ?? 0) === $sectionId) {
                $records[$recordIndex]['position'] = $targetPosition;
                $records[$recordIndex]['updated_at'] = date('c');
            }

            if ((int) ($record['id'] ?? 0) === (int) $sections[$target]['id']) {
                $records[$recordIndex]['position'] = $currentPosition;
                $records[$recordIndex]['updated_at'] = date('c');
            }
        }

        $this->sections->save($records);
        $this->normalizeSectionPositions($courseId);

        return ['ok' => true];
    }

    public function addLesson(int $courseId, array $data, array $files, array $user): array
    {
        $course = $this->courses->find($courseId);

        if (!$course || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Curso nao encontrado ou sem permissao.'];
        }

        $sectionId = (int) ($data['section_id'] ?? 0);
        $section = $this->section($sectionId);

        if (!$section || (int) ($section['course_id'] ?? 0) !== $courseId) {
            return ['ok' => false, 'error' => 'Escolha um modulo valido.'];
        }

        $title = trim($data['title'] ?? '');
        $type = $data['type'] ?? 'video';

        if ($title === '') {
            return ['ok' => false, 'error' => 'O titulo da aula e obrigatorio.'];
        }

        if (!in_array($type, ['video', 'text', 'pdf'], true)) {
            return ['ok' => false, 'error' => 'Tipo de aula invalido.'];
        }

        $storedFile = null;
        $textContent = trim($data['text_content'] ?? '');

        if ($type === 'video') {
            $storedFile = $this->storeUpload($files['lesson_file'] ?? null, 'videos', ['video/mp4', 'video/webm', 'video/ogg'], 500 * 1024 * 1024);
        } elseif ($type === 'pdf') {
            $storedFile = $this->storeUpload($files['lesson_file'] ?? null, 'materials', ['application/pdf'], 40 * 1024 * 1024);
        } elseif ($textContent === '') {
            return ['ok' => false, 'error' => 'A aula em texto precisa de conteudo.'];
        }

        if (is_array($storedFile) && !$storedFile['ok']) {
            return ['ok' => false, 'error' => $storedFile['error']];
        }

        $lesson = $this->lessons->insert([
            'course_id' => $courseId,
            'section_id' => $sectionId,
            'title' => $title,
            'description' => trim($data['description'] ?? ''),
            'type' => $type,
            'video_path' => $type === 'video' ? $storedFile['path'] : '',
            'pdf_path' => $type === 'pdf' ? $storedFile['path'] : '',
            'original_file_name' => $storedFile['original_name'] ?? '',
            'mime_type' => $storedFile['mime_type'] ?? '',
            'text_content' => $type === 'text' ? $textContent : '',
            'duration_seconds' => (int) ($data['duration_seconds'] ?? 0),
            'is_preview' => !empty($data['is_preview']),
            'position' => $this->nextLessonPosition($sectionId),
            'status' => 'published',
            'created_at' => date('c'),
            'updated_at' => date('c'),
        ]);

        $this->storeMaterials((int) $lesson['id'], $files['materials'] ?? null);
        $this->logs->log('lesson_created', "Aula {$title} criada.", (int) $user['id']);

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

    public function updateLesson(int $courseId, int $lessonId, array $data, array $user): array
    {
        $course = $this->courses->find($courseId);
        $lesson = $this->lesson($lessonId);

        if (!$course || !$lesson || (int) ($lesson['course_id'] ?? 0) !== $courseId || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Aula nao encontrada ou sem permissao.'];
        }

        $title = trim($data['title'] ?? '');

        if ($title === '') {
            return ['ok' => false, 'error' => 'O titulo da aula e obrigatorio.'];
        }

        $updated = $this->updateLessonRecord($lessonId, [
            'title' => $title,
            'description' => trim($data['description'] ?? ''),
            'duration_seconds' => (int) ($data['duration_seconds'] ?? 0),
            'is_preview' => !empty($data['is_preview']),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('lesson_updated', "Aula {$title} actualizada.", (int) $user['id']);

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

    public function deleteLesson(int $courseId, int $lessonId, array $user): array
    {
        $course = $this->courses->find($courseId);
        $lesson = $this->lesson($lessonId);

        if (!$course || !$lesson || (int) ($lesson['course_id'] ?? 0) !== $courseId || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Aula nao encontrada ou sem permissao.'];
        }

        $sectionId = (int) $lesson['section_id'];
        $this->deleteLessonRecord($lessonId);
        $this->normalizeLessonPositions($sectionId);
        $this->logs->log('lesson_deleted', "Aula {$lesson['title']} removida.", (int) $user['id']);

        return ['ok' => true];
    }

    public function moveLesson(int $courseId, int $lessonId, string $direction, array $user): array
    {
        $course = $this->courses->find($courseId);
        $lesson = $this->lesson($lessonId);

        if (!$course || !$lesson || (int) ($lesson['course_id'] ?? 0) !== $courseId || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Aula nao encontrada ou sem permissao.'];
        }

        $sectionId = (int) $lesson['section_id'];
        $lessons = array_values(array_filter(
            $this->lessons->all(),
            fn (array $item): bool => (int) ($item['section_id'] ?? 0) === $sectionId
        ));
        usort($lessons, fn (array $a, array $b): int => (int) $a['position'] <=> (int) $b['position']);
        $index = array_search($lessonId, array_map(fn (array $item): int => (int) $item['id'], $lessons), true);
        $target = $direction === 'up' ? $index - 1 : $index + 1;

        if ($index === false || !isset($lessons[$target])) {
            return ['ok' => true];
        }

        $currentPosition = (int) $lessons[$index]['position'];
        $targetPosition = (int) $lessons[$target]['position'];
        $records = $this->lessons->all();

        foreach ($records as $recordIndex => $record) {
            if ((int) ($record['id'] ?? 0) === $lessonId) {
                $records[$recordIndex]['position'] = $targetPosition;
                $records[$recordIndex]['updated_at'] = date('c');
            }

            if ((int) ($record['id'] ?? 0) === (int) $lessons[$target]['id']) {
                $records[$recordIndex]['position'] = $currentPosition;
                $records[$recordIndex]['updated_at'] = date('c');
            }
        }

        $this->lessons->save($records);
        $this->normalizeLessonPositions($sectionId);

        return ['ok' => true];
    }

    public function lesson(int $lessonId): ?array
    {
        foreach ($this->lessons->all() as $lesson) {
            if ((int) ($lesson['id'] ?? 0) === $lessonId) {
                $lesson['materials'] = $this->lessonMaterials($lessonId);
                return $lesson;
            }
        }

        return null;
    }

    public function material(int $materialId): ?array
    {
        foreach ($this->materials->all() as $material) {
            if ((int) ($material['id'] ?? 0) === $materialId) {
                return $material;
            }
        }

        return null;
    }

    public function canAccessLesson(array $lesson, array $user): bool
    {
        $course = $this->courses->find((int) ($lesson['course_id'] ?? 0));

        if (!$course) {
            return false;
        }

        if ($this->courses->canManage($course, $user)) {
            return true;
        }

        if (($user['role'] ?? '') === 'student') {
            $enrollments = new EnrollmentService();

            if ($enrollments->hasAccess((int) $user['id'], (int) $lesson['course_id'])) {
                return true;
            }
        }

        return !empty($lesson['is_preview']) && ($course['status'] ?? '') === 'published';
    }

    public function absoluteFilePath(string $relativePath): string
    {
        return storage_path($relativePath);
    }

    private function section(int $sectionId): ?array
    {
        foreach ($this->sections->all() as $section) {
            if ((int) ($section['id'] ?? 0) === $sectionId) {
                return $section;
            }
        }

        return null;
    }

    private function updateSectionRecord(int $sectionId, array $changes): array
    {
        $records = $this->sections->all();
        $updated = [];

        foreach ($records as $index => $section) {
            if ((int) ($section['id'] ?? 0) === $sectionId) {
                $records[$index] = array_merge($section, $changes);
                $updated = $records[$index];
                break;
            }
        }

        $this->sections->save($records);

        return $updated;
    }

    private function updateLessonRecord(int $lessonId, array $changes): array
    {
        $records = $this->lessons->all();
        $updated = [];

        foreach ($records as $index => $lesson) {
            if ((int) ($lesson['id'] ?? 0) === $lessonId) {
                $records[$index] = array_merge($lesson, $changes);
                $updated = $records[$index];
                break;
            }
        }

        $this->lessons->save($records);

        return $updated;
    }

    private function deleteLessonRecord(int $lessonId): void
    {
        $lesson = $this->lesson($lessonId);

        if ($lesson) {
            foreach (['video_path', 'pdf_path'] as $pathKey) {
                if (!empty($lesson[$pathKey])) {
                    $this->deleteStoredFile((string) $lesson[$pathKey]);
                }
            }

            foreach ($lesson['materials'] ?? [] as $material) {
                $this->deleteStoredFile((string) ($material['file_path'] ?? ''));
            }
        }

        $this->lessons->save(array_filter(
            $this->lessons->all(),
            fn (array $item): bool => (int) ($item['id'] ?? 0) !== $lessonId
        ));
        $this->materials->save(array_filter(
            $this->materials->all(),
            fn (array $item): bool => (int) ($item['lesson_id'] ?? 0) !== $lessonId
        ));
    }

    private function normalizeSectionPositions(int $courseId): void
    {
        $records = $this->sections->all();
        $positions = array_values(array_filter(
            $records,
            fn (array $section): bool => (int) ($section['course_id'] ?? 0) === $courseId
        ));
        usort($positions, fn (array $a, array $b): int => (int) $a['position'] <=> (int) $b['position']);

        foreach ($positions as $position => $section) {
            foreach ($records as $index => $record) {
                if ((int) ($record['id'] ?? 0) === (int) $section['id']) {
                    $records[$index]['position'] = $position + 1;
                }
            }
        }

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

    private function normalizeLessonPositions(int $sectionId): void
    {
        $records = $this->lessons->all();
        $positions = array_values(array_filter(
            $records,
            fn (array $lesson): bool => (int) ($lesson['section_id'] ?? 0) === $sectionId
        ));
        usort($positions, fn (array $a, array $b): int => (int) $a['position'] <=> (int) $b['position']);

        foreach ($positions as $position => $lesson) {
            foreach ($records as $index => $record) {
                if ((int) ($record['id'] ?? 0) === (int) $lesson['id']) {
                    $records[$index]['position'] = $position + 1;
                }
            }
        }

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

    private function deleteStoredFile(string $relativePath): void
    {
        if ($relativePath === '') {
            return;
        }

        $path = storage_path($relativePath);

        if (is_file($path)) {
            unlink($path);
        }
    }

    private function lessonMaterials(int $lessonId): array
    {
        return array_values(array_filter(
            $this->materials->all(),
            fn (array $material): bool => (int) ($material['lesson_id'] ?? 0) === $lessonId
        ));
    }

    private function nextSectionPosition(int $courseId): int
    {
        $positions = array_map(
            fn (array $section): int => (int) ($section['position'] ?? 0),
            array_filter($this->sections->all(), fn (array $section): bool => (int) ($section['course_id'] ?? 0) === $courseId)
        );

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

    private function nextLessonPosition(int $sectionId): int
    {
        $positions = array_map(
            fn (array $lesson): int => (int) ($lesson['position'] ?? 0),
            array_filter($this->lessons->all(), fn (array $lesson): bool => (int) ($lesson['section_id'] ?? 0) === $sectionId)
        );

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

    private function storeMaterials(int $lessonId, ?array $materials): void
    {
        if (!$materials || empty($materials['name']) || !is_array($materials['name'])) {
            return;
        }

        foreach ($materials['name'] as $index => $name) {
            if (($materials['error'][$index] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
                continue;
            }

            $file = [
                'name' => $name,
                'type' => $materials['type'][$index] ?? '',
                'tmp_name' => $materials['tmp_name'][$index] ?? '',
                'error' => $materials['error'][$index] ?? UPLOAD_ERR_NO_FILE,
                'size' => $materials['size'][$index] ?? 0,
            ];

            $stored = $this->storeUpload($file, 'materials', ['application/pdf', 'image/jpeg', 'image/png', 'text/plain'], 40 * 1024 * 1024);

            if ($stored['ok']) {
                $this->materials->insert([
                    'lesson_id' => $lessonId,
                    'title' => pathinfo($stored['original_name'], PATHINFO_FILENAME),
                    'file_path' => $stored['path'],
                    'file_type' => $stored['mime_type'],
                    'file_size' => $stored['size'],
                    'is_downloadable' => true,
                    'created_at' => date('c'),
                    'updated_at' => date('c'),
                ]);
            }
        }
    }

    private function storeUpload(?array $file, string $folder, array $allowedTypes, int $maxSize): array
    {
        if (!$file || ($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
            return ['ok' => false, 'error' => 'Envie o ficheiro da aula.'];
        }

        if (($file['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) {
            return ['ok' => false, 'error' => 'Falha no upload do ficheiro.'];
        }

        if ((int) ($file['size'] ?? 0) > $maxSize) {
            return ['ok' => false, 'error' => 'O ficheiro excede o tamanho permitido.'];
        }

        $tmpName = (string) ($file['tmp_name'] ?? '');
        $mimeType = mime_content_type($tmpName) ?: ($file['type'] ?? '');

        if (!in_array($mimeType, $allowedTypes, true)) {
            return ['ok' => false, 'error' => 'Tipo de ficheiro nao permitido: ' . $mimeType];
        }

        $extension = strtolower(pathinfo((string) $file['name'], PATHINFO_EXTENSION));
        $safeName = bin2hex(random_bytes(16)) . ($extension ? '.' . $extension : '');
        $relativePath = $folder . DIRECTORY_SEPARATOR . $safeName;
        $destination = storage_path($relativePath);

        if (!is_dir(dirname($destination))) {
            mkdir(dirname($destination), 0775, true);
        }

        if (!move_uploaded_file($tmpName, $destination)) {
            return ['ok' => false, 'error' => 'Nao foi possivel guardar o ficheiro.'];
        }

        return [
            'ok' => true,
            'path' => $relativePath,
            'original_name' => (string) $file['name'],
            'mime_type' => $mimeType,
            'size' => (int) $file['size'],
        ];
    }
}
