<?php
class VideoLesson
{
    public static function all(): array
    {
        Database::migrate();
        $stmt = Database::pdo()->query('SELECT * FROM video_lessons ORDER BY is_active DESC, id DESC');
        return $stmt->fetchAll();
    }

    public static function save(array $data, array $videoFile = [], array $coverFile = []): array
    {
        Database::migrate();
        @set_time_limit(0);

        $id = (int)($data['video_lesson_id'] ?? 0);
        $title = trim((string)($data['title'] ?? ''));
        $description = trim((string)($data['description'] ?? ''));
        $sourceType = ($data['source_type'] ?? 'link') === 'upload' ? 'upload' : 'link';
        $videoUrl = trim((string)($data['video_url'] ?? ''));
        $audience = trim((string)($data['audience'] ?? 'student'));
        $accessMode = ($data['access_mode'] ?? 'individual') === 'all' ? 'all' : 'individual';
        $createdByProfessionalId = max(0, (int)($data['created_by_professional_id'] ?? 0));

        if (!in_array($audience, ['student','professional','beginner','all'], true)) {
            $audience = 'student';
        }
        if ($title === '') {
            return ['ok' => false, 'message' => 'Informe o título da videoaula.'];
        }

        $pdo = Database::pdo();
        $existing = null;
        if ($id > 0) {
            $stmt = $pdo->prepare('SELECT * FROM video_lessons WHERE id = ?');
            $stmt->execute([$id]);
            $existing = $stmt->fetch();
            if ($existing && $createdByProfessionalId <= 0) { $createdByProfessionalId = (int)($existing['created_by_professional_id'] ?? 0); }
            if (!$existing) {
                return ['ok' => false, 'message' => 'Videoaula não encontrada.'];
            }
        }

        $oldFilePath = $existing['file_path'] ?? null;
        $oldCoverPath = $existing['cover_path'] ?? null;
        $filePath = $oldFilePath;
        $originalName = $existing['original_name'] ?? null;
        $mimeType = $existing['mime_type'] ?? null;
        $fileSize = $existing['file_size'] ?? null;
        $coverPath = $oldCoverPath;
        $newFilePath = null;
        $newCoverPath = null;
        $deleteOldFileAfterSave = false;
        $deleteOldCoverAfterSave = false;

        $chunkToken = trim((string)($data['uploaded_video_token'] ?? ''));
        if ($sourceType === 'upload' && $chunkToken !== '') {
            $saved = PortalMedia::finalizeVideoChunks($chunkToken, 'video_lessons');
            if (empty($saved['ok'])) {
                return $saved;
            }
            $filePath = $newFilePath = $saved['path'];
            $originalName = $saved['original_name'];
            $mimeType = $saved['mime_type'];
            $fileSize = $saved['file_size'];
            $videoUrl = '';
            $deleteOldFileAfterSave = !empty($oldFilePath) && $oldFilePath !== $filePath;
        } elseif ($sourceType === 'upload' && !empty($videoFile['tmp_name'])) {
            $saved = PortalMedia::saveVideo($videoFile, 'video_lessons');
            if (empty($saved['ok'])) {
                return $saved;
            }
            $filePath = $newFilePath = $saved['path'];
            $originalName = $saved['original_name'];
            $mimeType = $saved['mime_type'];
            $fileSize = $saved['file_size'];
            $videoUrl = '';
            $deleteOldFileAfterSave = !empty($oldFilePath) && $oldFilePath !== $filePath;
        } elseif ($sourceType === 'upload' && !$filePath) {
            return ['ok' => false, 'message' => 'Envie o arquivo de vídeo.'];
        } elseif ($sourceType === 'link') {
            if (!is_valid_http_url($videoUrl)) {
                return ['ok' => false, 'message' => 'Informe um link de vídeo válido com http ou https.'];
            }
            $filePath = null;
            $originalName = null;
            $mimeType = null;
            $fileSize = null;
            $deleteOldFileAfterSave = !empty($oldFilePath);
        }

        if (!empty($coverFile['tmp_name'])) {
            $cover = PortalMedia::saveImage($coverFile, 'video_covers');
            if (empty($cover['ok'])) {
                if ($newFilePath) {
                    PortalMedia::delete($newFilePath);
                }
                return $cover;
            }
            $coverPath = $newCoverPath = $cover['path'];
            $deleteOldCoverAfterSave = !empty($oldCoverPath) && $oldCoverPath !== $coverPath;
        }

        try {
            if ($id > 0) {
                $stmt = $pdo->prepare('UPDATE video_lessons SET title=?, description=?, source_type=?, video_url=?, file_path=?, original_name=?, mime_type=?, file_size=?, cover_path=?, audience=?, access_mode=?, created_by_professional_id=? WHERE id=?');
                $stmt->execute([$title,$description,$sourceType,$videoUrl ?: null,$filePath,$originalName,$mimeType,$fileSize,$coverPath,$audience,$accessMode,$createdByProfessionalId ?: null,$id]);
                $message = 'Videoaula atualizada.';
            } else {
                $stmt = $pdo->prepare('INSERT INTO video_lessons (title,description,source_type,video_url,file_path,original_name,mime_type,file_size,cover_path,audience,access_mode,created_by_professional_id,is_active,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,1,?)');
                $stmt->execute([$title,$description,$sourceType,$videoUrl ?: null,$filePath,$originalName,$mimeType,$fileSize,$coverPath,$audience,$accessMode,$createdByProfessionalId ?: null,date('Y-m-d H:i:s')]);
                $id = (int)$pdo->lastInsertId();
                $message = 'Videoaula cadastrada.';
            }
        } catch (Throwable $e) {
            if ($newFilePath) {
                PortalMedia::delete($newFilePath);
            }
            if ($newCoverPath) {
                PortalMedia::delete($newCoverPath);
            }
            return ['ok' => false, 'message' => 'Não foi possível salvar a videoaula: ' . $e->getMessage()];
        }

        if ($deleteOldFileAfterSave) {
            PortalMedia::delete($oldFilePath);
        }
        if ($deleteOldCoverAfterSave) {
            PortalMedia::delete($oldCoverPath);
        }
        return ['ok' => true, 'message' => $message, 'video_lesson_id' => $id];
    }

    public static function forProfessionalManager(int $professionalId): array
    {
        Database::migrate();
        $stmt=Database::pdo()->prepare('SELECT * FROM video_lessons WHERE created_by_professional_id=? ORDER BY is_active DESC,id DESC');
        $stmt->execute([$professionalId]);
        return $stmt->fetchAll();
    }

    public static function deleteByProfessional(int $lessonId,int $professionalId): bool
    {
        Database::migrate();
        $stmt=Database::pdo()->prepare('SELECT id FROM video_lessons WHERE id=? AND created_by_professional_id=? LIMIT 1');
        $stmt->execute([$lessonId,$professionalId]);
        return $stmt->fetchColumn() ? self::delete($lessonId) : false;
    }

    public static function toggle(int $id): bool
    {
        Database::migrate();
        $stmt = Database::pdo()->prepare('UPDATE video_lessons SET is_active = CASE WHEN is_active=1 THEN 0 ELSE 1 END WHERE id=?');
        $stmt->execute([$id]);
        return $stmt->rowCount() > 0;
    }

    public static function delete(int $id): bool
    {
        Database::migrate();
        $stmt = Database::pdo()->prepare('SELECT file_path, cover_path FROM video_lessons WHERE id=?');
        $stmt->execute([$id]);
        $row = $stmt->fetch();
        if (!$row) return false;
        $del = Database::pdo()->prepare('DELETE FROM video_lessons WHERE id=?');
        $del->execute([$id]);
        if ($del->rowCount() > 0) {
            PortalMedia::delete($row['file_path'] ?? '');
            PortalMedia::delete($row['cover_path'] ?? '');
            return true;
        }
        return false;
    }

    public static function grant(int $lessonId, string $recipientType, int $recipientId): bool
    {
        Database::migrate();
        $recipientType = trim($recipientType);
        if (!in_array($recipientType, ['student','professional','beginner','group'], true) || $recipientId <= 0) return false;
        $lessonStmt = Database::pdo()->prepare("SELECT audience, access_mode FROM video_lessons WHERE id=? LIMIT 1");
        $lessonStmt->execute([$lessonId]);
        $lesson = $lessonStmt->fetch();
        if (!$lesson || ($lesson['access_mode'] ?? '') !== 'individual') return false;
        if ($recipientType === 'group') {
            if (!in_array(($lesson['audience'] ?? 'student'), ['student','all'], true) || !TheoryGroup::find($recipientId)) return false;
        } else {
            if (($lesson['audience'] ?? 'student') !== 'all' && ($lesson['audience'] ?? '') !== $recipientType) return false;
            $recipient = Communication::actorByTypeAndId($recipientType, $recipientId);
            if (!$recipient || ($recipient['actor_type'] ?? '') !== $recipientType) return false;
        }
        if (Database::driver() === 'mysql') {
            $stmt = Database::pdo()->prepare('INSERT IGNORE INTO video_lesson_access (video_lesson_id,recipient_type,recipient_id,granted_at) VALUES (?,?,?,?)');
        } else {
            $stmt = Database::pdo()->prepare('INSERT OR IGNORE INTO video_lesson_access (video_lesson_id,recipient_type,recipient_id,granted_at) VALUES (?,?,?,?)');
        }
        $stmt->execute([$lessonId,$recipientType,$recipientId,date('Y-m-d H:i:s')]);
        return true;
    }

    public static function revoke(int $lessonId, string $recipientType, int $recipientId): bool
    {
        Database::migrate();
        $recipientType = trim($recipientType);
        if (!in_array($recipientType, ['student','professional','beginner','group'], true)) return false;
        $stmt = Database::pdo()->prepare('DELETE FROM video_lesson_access WHERE video_lesson_id=? AND recipient_type=? AND recipient_id=?');
        $stmt->execute([$lessonId,$recipientType,$recipientId]);
        return true;
    }

    public static function accessMap(): array
    {
        Database::migrate();
        $map = [];
        foreach (Database::pdo()->query('SELECT * FROM video_lesson_access')->fetchAll() as $row) {
            $map[(int)$row['video_lesson_id']][$row['recipient_type']][(int)$row['recipient_id']] = true;
        }
        return $map;
    }

    public static function forActor(string $actorType, int $actorId): array
    {
        Database::migrate();
        $actorType = normalize_internal_actor_type($actorType);
        if ($actorType === 'student') {
            $stmt = Database::pdo()->prepare("SELECT DISTINCT v.* FROM video_lessons v
                LEFT JOIN video_lesson_access direct_access ON direct_access.video_lesson_id=v.id AND direct_access.recipient_type='student' AND direct_access.recipient_id=?
                LEFT JOIN video_lesson_access group_access ON group_access.video_lesson_id=v.id AND group_access.recipient_type='group'
                LEFT JOIN theory_group_members gm ON gm.group_id=group_access.recipient_id AND gm.student_id=?
                WHERE v.is_active=1 AND (v.audience='all' OR v.audience='student')
                  AND (v.access_mode='all' OR direct_access.id IS NOT NULL OR gm.id IS NOT NULL)
                ORDER BY v.id DESC");
            $stmt->execute([$actorId,$actorId]);
            return $stmt->fetchAll();
        }
        if ($actorType === 'professional') {
            $stmt = Database::pdo()->prepare("SELECT DISTINCT v.* FROM video_lessons v
                LEFT JOIN video_lesson_access direct_access ON direct_access.video_lesson_id=v.id AND direct_access.recipient_type='professional' AND direct_access.recipient_id=?
                LEFT JOIN video_lesson_access group_access ON group_access.video_lesson_id=v.id AND group_access.recipient_type='group'
                LEFT JOIN theory_groups g ON g.id=group_access.recipient_id AND g.instructor_id=? AND g.is_active=1
                WHERE v.is_active=1 AND (v.audience='all' OR v.audience='professional' OR (v.audience='student' AND g.id IS NOT NULL))
                  AND (v.access_mode='all' OR direct_access.id IS NOT NULL OR g.id IS NOT NULL)
                ORDER BY v.id DESC");
            $stmt->execute([$actorId,$actorId]);
            return $stmt->fetchAll();
        }
        $stmt = Database::pdo()->prepare("SELECT DISTINCT v.* FROM video_lessons v
            LEFT JOIN video_lesson_access a ON a.video_lesson_id=v.id AND a.recipient_type=? AND a.recipient_id=?
            WHERE v.is_active=1 AND (v.audience='all' OR v.audience=?) AND (v.access_mode='all' OR a.id IS NOT NULL)
            ORDER BY v.id DESC");
        $stmt->execute([$actorType,$actorId,$actorType]);
        return $stmt->fetchAll();
    }


    public static function forGroup(int $groupId): array
    {
        Database::migrate();
        if ($groupId <= 0 || !TheoryGroup::find($groupId)) {
            return [];
        }
        $stmt = Database::pdo()->prepare("SELECT DISTINCT v.* FROM video_lessons v
            INNER JOIN video_lesson_access a ON a.video_lesson_id=v.id
            WHERE v.is_active=1 AND a.recipient_type='group' AND a.recipient_id=?
            ORDER BY v.id DESC");
        $stmt->execute([$groupId]);
        return $stmt->fetchAll();
    }

    public static function findForActor(int $lessonId, string $actorType, int $actorId): ?array
    {
        foreach (self::forActor($actorType, $actorId) as $lesson) {
            if ((int)$lesson['id'] === $lessonId) return $lesson;
        }
        return null;
    }

    public static function stream(array $lesson): never
    {
        $path = ROOT_PATH . '/' . ltrim((string)($lesson['file_path'] ?? ''), '/');
        $realRoot = realpath(UPLOAD_PATH);
        $realPath = realpath($path);
        if (!$realRoot || !$realPath || !str_starts_with($realPath, $realRoot . DIRECTORY_SEPARATOR) || !is_file($realPath)) {
            http_response_code(404);
            exit('Arquivo de vídeo não encontrado.');
        }

        $size = (int)filesize($realPath);
        $start = 0;
        $end = max(0, $size - 1);
        $status = 200;
        $range = trim((string)($_SERVER['HTTP_RANGE'] ?? ''));
        if ($range !== '' && preg_match('/bytes=(\d*)-(\d*)/i', $range, $match)) {
            if ($match[1] === '' && $match[2] !== '') {
                $length = min($size, (int)$match[2]);
                $start = max(0, $size - $length);
            } else {
                $start = max(0, (int)($match[1] ?: 0));
                if ($match[2] !== '') {
                    $end = min($end, (int)$match[2]);
                }
            }
            if ($start > $end || $start >= $size) {
                header('Content-Range: bytes */' . $size);
                http_response_code(416);
                exit;
            }
            $status = 206;
        }

        $length = $end - $start + 1;
        http_response_code($status);
        header('Content-Type: ' . (($lesson['mime_type'] ?? '') ?: 'video/mp4'));
        header('Accept-Ranges: bytes');
        header('Content-Length: ' . $length);
        if ($status === 206) {
            header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
        }
        $downloadName = str_replace(['"', "\r", "\n"], '', (string)($lesson['original_name'] ?? 'video.mp4'));
        header('Content-Disposition: inline; filename="' . $downloadName . '"');
        header('Cache-Control: private, max-age=3600');

        $handle = fopen($realPath, 'rb');
        if (!$handle) {
            http_response_code(500);
            exit('Não foi possível abrir o vídeo.');
        }
        fseek($handle, $start);
        $remaining = $length;
        while ($remaining > 0 && !feof($handle)) {
            $chunk = fread($handle, min(1024 * 1024, $remaining));
            if ($chunk === false || $chunk === '') break;
            echo $chunk;
            $remaining -= strlen($chunk);
            flush();
        }
        fclose($handle);
        exit;
    }
}
