<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Controller;
use App\Core\Request;
use App\Services\CourseContentService;
use App\Services\CourseService;

class CourseContentController extends Controller
{
    private CourseService $courses;
    private CourseContentService $content;

    public function __construct()
    {
        $this->courses = new CourseService();
        $this->content = new CourseContentService();
    }

    public function index(Request $request): string
    {
        $this->requireCreator();
        $course = $this->courseFromRequest($request);

        return $this->view('courses/content/index', [
            'title' => 'Conteudo do curso',
            'course' => $course,
            'sections' => $this->content->courseContent((int) $course['id']),
        ]);
    }

    public function storeSection(Request $request): void
    {
        $this->requireCreator();
        $this->guardCsrf($request);
        $course = $this->courseFromRequest($request);

        $result = $this->content->addSection((int) $course['id'], $request->all(), current_user());

        flash($result['ok'] ? 'success' : 'error', $result['ok'] ? 'Modulo criado.' : $result['error']);
        app()->response()->redirect('/manage/courses/content?id=' . $course['id']);
    }

    public function updateSection(Request $request): void
    {
        $this->requireCreator();
        $this->guardCsrf($request);
        $course = $this->courseFromRequest($request);

        $result = $this->content->updateSection((int) $course['id'], (int) $request->post('section_id'), $request->all(), current_user());

        flash($result['ok'] ? 'success' : 'error', $result['ok'] ? 'Modulo actualizado.' : $result['error']);
        app()->response()->redirect('/manage/courses/content?id=' . $course['id']);
    }

    public function deleteSection(Request $request): void
    {
        $this->requireCreator();
        $this->guardCsrf($request);
        $course = $this->courseFromRequest($request);

        $result = $this->content->deleteSection((int) $course['id'], (int) $request->post('section_id'), current_user());

        flash($result['ok'] ? 'success' : 'error', $result['ok'] ? 'Modulo removido.' : $result['error']);
        app()->response()->redirect('/manage/courses/content?id=' . $course['id']);
    }

    public function moveSection(Request $request): void
    {
        $this->requireCreator();
        $this->guardCsrf($request);
        $course = $this->courseFromRequest($request);

        $result = $this->content->moveSection((int) $course['id'], (int) $request->post('section_id'), (string) $request->post('direction', 'down'), current_user());

        if (!$result['ok']) {
            flash('error', $result['error']);
        }

        app()->response()->redirect('/manage/courses/content?id=' . $course['id']);
    }

    public function storeLesson(Request $request): void
    {
        $this->requireCreator();
        $this->guardCsrf($request);
        $course = $this->courseFromRequest($request);

        $result = $this->content->addLesson((int) $course['id'], $request->all(), $_FILES, current_user());

        flash($result['ok'] ? 'success' : 'error', $result['ok'] ? 'Aula criada.' : $result['error']);
        app()->response()->redirect('/manage/courses/content?id=' . $course['id']);
    }

    public function updateLesson(Request $request): void
    {
        $this->requireCreator();
        $this->guardCsrf($request);
        $course = $this->courseFromRequest($request);

        $result = $this->content->updateLesson((int) $course['id'], (int) $request->post('lesson_id'), $request->all(), current_user());

        flash($result['ok'] ? 'success' : 'error', $result['ok'] ? 'Aula actualizada.' : $result['error']);
        app()->response()->redirect('/manage/courses/content?id=' . $course['id']);
    }

    public function deleteLesson(Request $request): void
    {
        $this->requireCreator();
        $this->guardCsrf($request);
        $course = $this->courseFromRequest($request);

        $result = $this->content->deleteLesson((int) $course['id'], (int) $request->post('lesson_id'), current_user());

        flash($result['ok'] ? 'success' : 'error', $result['ok'] ? 'Aula removida.' : $result['error']);
        app()->response()->redirect('/manage/courses/content?id=' . $course['id']);
    }

    public function moveLesson(Request $request): void
    {
        $this->requireCreator();
        $this->guardCsrf($request);
        $course = $this->courseFromRequest($request);

        $result = $this->content->moveLesson((int) $course['id'], (int) $request->post('lesson_id'), (string) $request->post('direction', 'down'), current_user());

        if (!$result['ok']) {
            flash('error', $result['error']);
        }

        app()->response()->redirect('/manage/courses/content?id=' . $course['id']);
    }

    public function preview(Request $request): string
    {
        require_auth();
        $lesson = $this->content->lesson((int) $request->input('lesson_id'));

        if (!$lesson || !$this->content->canAccessLesson($lesson, current_user())) {
            flash('error', 'Aula indisponivel.');
            app()->response()->redirect('/dashboard');
        }

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

        return $this->view('courses/content/preview', [
            'title' => $lesson['title'],
            'course' => $course,
            'lesson' => $lesson,
        ]);
    }

    public function video(Request $request): void
    {
        require_auth();
        $lesson = $this->content->lesson((int) $request->input('lesson_id'));

        if (!$lesson || ($lesson['type'] ?? '') !== 'video' || !$this->content->canAccessLesson($lesson, current_user())) {
            http_response_code(403);
            exit('Acesso negado.');
        }

        $this->streamFile($this->content->absoluteFilePath((string) $lesson['video_path']), (string) ($lesson['mime_type'] ?? 'video/mp4'), false);
    }

    public function lessonFile(Request $request): void
    {
        require_auth();
        $lesson = $this->content->lesson((int) $request->input('lesson_id'));

        if (!$lesson || ($lesson['type'] ?? '') !== 'pdf' || !$this->content->canAccessLesson($lesson, current_user())) {
            http_response_code(403);
            exit('Acesso negado.');
        }

        $this->streamFile($this->content->absoluteFilePath((string) $lesson['pdf_path']), 'application/pdf', false);
    }

    public function material(Request $request): void
    {
        require_auth();
        $material = $this->content->material((int) $request->input('material_id'));
        $lesson = $material ? $this->content->lesson((int) $material['lesson_id']) : null;

        if (!$material || !$lesson || !$this->content->canAccessLesson($lesson, current_user())) {
            http_response_code(403);
            exit('Acesso negado.');
        }

        $this->streamFile($this->content->absoluteFilePath((string) $material['file_path']), (string) $material['file_type'], true);
    }

    private function courseFromRequest(Request $request): array
    {
        $course = $this->courses->find((int) $request->input('id', $request->post('course_id')));

        if (!$course || !$this->courses->canManage($course, current_user())) {
            flash('error', 'Curso nao encontrado.');
            app()->response()->redirect('/manage/courses');
        }

        return $course;
    }

    private function requireCreator(): void
    {
        require_auth();

        if (!in_array(current_user()['role'] ?? '', ['admin', 'instructor', 'institution'], true)) {
            flash('error', 'Apenas instrutores, instituicoes e admin podem gerir conteudo.');
            app()->response()->redirect('/dashboard');
        }
    }

    private function guardCsrf(Request $request): void
    {
        if (!verify_csrf((string) $request->post('_csrf_token', ''))) {
            flash('error', 'Sessao expirada. Tente novamente.');
            app()->response()->back();
        }
    }

    private function streamFile(string $path, string $mimeType, bool $download): void
    {
        if (!is_file($path)) {
            http_response_code(404);
            exit('Ficheiro nao encontrado.');
        }

        $size = filesize($path);
        $start = 0;
        $end = $size - 1;

        header('X-Content-Type-Options: nosniff');
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
        header('Pragma: no-cache');
        header('Content-Type: ' . $mimeType);
        header('Accept-Ranges: bytes');
        header('Content-Disposition: ' . ($download ? 'attachment' : 'inline') . '; filename="conteudo"');

        if (isset($_SERVER['HTTP_RANGE']) && preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches)) {
            $start = (int) $matches[1];
            $end = $matches[2] !== '' ? (int) $matches[2] : $end;
            http_response_code(206);
            header("Content-Range: bytes {$start}-{$end}/{$size}");
        }

        $length = $end - $start + 1;
        header('Content-Length: ' . $length);

        $handle = fopen($path, 'rb');
        fseek($handle, $start);
        $buffer = 8192;
        $sent = 0;

        while (!feof($handle) && $sent < $length) {
            $read = min($buffer, $length - $sent);
            echo fread($handle, $read);
            $sent += $read;
            flush();
        }

        fclose($handle);
        exit;
    }
}
