<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\FileStore;

class CertificateService
{
    private FileStore $certificates;
    private AuthService $auth;
    private CourseService $courses;
    private EnrollmentService $enrollments;
    private AssessmentService $assessments;
    private ActivityLogService $logs;

    public function __construct()
    {
        $this->certificates = new FileStore(storage_path('data/certificates.json'));
        $this->auth = new AuthService();
        $this->courses = new CourseService();
        $this->enrollments = new EnrollmentService();
        $this->assessments = new AssessmentService();
        $this->logs = new ActivityLogService();
    }

    public function issueForEnrollment(int $enrollmentId, array $student): array
    {
        $enrollment = $this->enrollments->enrollmentById($enrollmentId);

        if (!$enrollment || (int) ($enrollment['student_id'] ?? 0) !== (int) $student['id']) {
            return ['ok' => false, 'error' => 'Matricula nao encontrada.'];
        }

        $eligibility = $this->eligibilityForEnrollment($enrollment);

        if (!$eligibility['eligible']) {
            return ['ok' => false, 'error' => $eligibility['reason']];
        }

        $existing = $this->findByEnrollment($enrollmentId);

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

        $course = $eligibility['course'];
        $progress = $eligibility['progress'];
        $code = $this->uniqueCode();

        $certificate = $this->certificates->insert([
            'enrollment_id' => $enrollmentId,
            'student_id' => (int) $student['id'],
            'student_name' => $student['name'] ?? 'Aluno',
            'student_email' => $student['email'] ?? '',
            'course_id' => (int) $course['id'],
            'course_title' => $course['title'],
            'issuer_type' => $course['owner_type'] ?? 'platform',
            'issuer_id' => (int) ($course['owner_id'] ?? 0),
            'issuer_name' => $this->issuerName($course),
            'workload_hours' => (int) ($course['workload_hours'] ?? 0),
            'completion_percentage' => (int) $progress['percentage'],
            'validation_code' => $code,
            'certificate_number' => 'CERT-' . date('Y') . '-' . str_pad((string) ($this->nextNumber()), 5, '0', STR_PAD_LEFT),
            'status' => 'issued',
            'issued_at' => date('c'),
            'reissued_at' => null,
            'created_at' => date('c'),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('certificate_issued', "Certificado {$certificate['certificate_number']} emitido.", (int) $student['id']);

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

    public function eligibilityForEnrollment(array $enrollment): array
    {
        $course = $this->courses->find((int) ($enrollment['course_id'] ?? 0));

        if (!$course) {
            return ['eligible' => false, 'reason' => 'Curso nao encontrado.', 'course' => null, 'progress' => null];
        }

        if (empty($course['certificate_enabled'])) {
            return ['eligible' => false, 'reason' => 'Este curso nao emite certificado.', 'course' => $course, 'progress' => null];
        }

        $progress = $this->enrollments->courseProgress((int) $enrollment['id']);
        $required = max(1, (int) ($course['completion_percentage_required'] ?? 100));

        if ((int) $progress['total'] <= 0) {
            return ['eligible' => false, 'reason' => 'O curso ainda nao tem aulas publicadas.', 'course' => $course, 'progress' => $progress];
        }

        if ((int) $progress['percentage'] < $required) {
            return [
                'eligible' => false,
                'reason' => "Conclua pelo menos {$required}% do curso para emitir o certificado.",
                'course' => $course,
                'progress' => $progress,
            ];
        }

        if (!$this->assessments->passedRequiredQuiz($course, (int) $enrollment['id'])) {
            $passScore = (int) ($course['final_quiz_pass_score'] ?? 70);
            return [
                'eligible' => false,
                'reason' => "Este curso exige teste final com nota minima de {$passScore}%.",
                'course' => $course,
                'progress' => $progress,
            ];
        }

        return ['eligible' => true, 'reason' => '', 'course' => $course, 'progress' => $progress];
    }

    public function certificatesForStudent(int $studentId): array
    {
        $items = array_values(array_filter(
            $this->certificates->all(),
            fn (array $certificate): bool => (int) ($certificate['student_id'] ?? 0) === $studentId
        ));

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

        return $items;
    }

    public function all(array $filters = []): array
    {
        $search = strtolower(trim((string) ($filters['search'] ?? '')));
        $items = $this->certificates->all();

        if ($search !== '') {
            $items = array_filter($items, function (array $certificate) use ($search): bool {
                $haystack = strtolower(
                    ($certificate['certificate_number'] ?? '') . ' ' .
                    ($certificate['validation_code'] ?? '') . ' ' .
                    ($certificate['student_name'] ?? '') . ' ' .
                    ($certificate['course_title'] ?? '') . ' ' .
                    ($certificate['issuer_name'] ?? '')
                );

                return str_contains($haystack, $search);
            });
        }

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

        return array_values($items);
    }

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

        return null;
    }

    public function findByEnrollment(int $enrollmentId): ?array
    {
        foreach ($this->certificates->all() as $certificate) {
            if ((int) ($certificate['enrollment_id'] ?? 0) === $enrollmentId) {
                return $certificate;
            }
        }

        return null;
    }

    public function findByCode(string $code): ?array
    {
        $code = strtoupper(trim($code));

        foreach ($this->certificates->all() as $certificate) {
            if (strtoupper((string) ($certificate['validation_code'] ?? '')) === $code) {
                return $certificate;
            }
        }

        return null;
    }

    public function reissue(int $id, int $adminId): array
    {
        $records = $this->certificates->all();
        $updated = null;

        foreach ($records as $index => $certificate) {
            if ((int) ($certificate['id'] ?? 0) === $id) {
                $records[$index]['status'] = 'issued';
                $records[$index]['reissued_at'] = date('c');
                $records[$index]['updated_at'] = date('c');
                $updated = $records[$index];
                break;
            }
        }

        if (!$updated) {
            return ['ok' => false, 'error' => 'Certificado nao encontrado.'];
        }

        $this->certificates->save($records);
        $this->logs->log('certificate_reissued', "Certificado {$updated['certificate_number']} reemitido.", $adminId);

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

    public function canUserSee(array $certificate, array $user): bool
    {
        if (($user['role'] ?? '') === 'admin') {
            return true;
        }

        return ($user['role'] ?? '') === 'student' && (int) ($certificate['student_id'] ?? 0) === (int) $user['id'];
    }

    private function issuerName(array $course): string
    {
        if (($course['owner_type'] ?? '') === 'platform') {
            return 'Moz Cursos';
        }

        if (($course['owner_type'] ?? '') === 'institution') {
            return $course['owner_name'] ?? 'Instituicao';
        }

        return $course['owner_name'] ?? 'Moz Cursos';
    }

    private function uniqueCode(): string
    {
        do {
            $code = 'MC-' . date('Y') . '-' . strtoupper(substr(bin2hex(random_bytes(4)), 0, 8));
        } while ($this->findByCode($code));

        return $code;
    }

    private function nextNumber(): int
    {
        $ids = array_column($this->certificates->all(), 'id');

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