<?php

namespace App\Services\ClassProgress;

use App\Models\ClassProgressAttachment;
use App\Models\ClassProgressReport;
use App\Models\Configuration;
use App\Models\TeacherClass;
use App\Models\User;
use App\Services\SchoolYears\SchoolYearContextService;
use Illuminate\Support\Facades\Schema;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

class ClassProgressQueryService
{
    private SchoolYearContextService $schoolYears;

    public function __construct($schoolYears = null)
    {
        $this->schoolYears = $schoolYears instanceof SchoolYearContextService
            ? $schoolYears
            : app(SchoolYearContextService::class);
    }

    public function listReports(User $user, array $filters): LengthAwarePaginator
    {
        $query = ClassProgressReport::query()
            ->with(['classSection', 'teacher'])
            ->orderBy($filters['sort_by'] ?? 'week_start', $filters['sort_dir'] ?? 'desc');

        if (!$this->isAdmin($user)) {
            $relaxedSectionIds = TeacherClass::distinctSectionIdsForTeacher((int) $user->id);

            if (! empty($filters['class_section_id'])) {
                $sid = (int) $filters['class_section_id'];
                // Section filter: if this user has ever taught the section, show all rows for that section
                // (legacy DB often has `class_progress_reports` without matching current-year `teacher_class`).
                if (! in_array($sid, $relaxedSectionIds, true)) {
                    $query->where('teacher_id', (int) $user->id);
                }
            } else {
                $query->where(function ($q) use ($user, $relaxedSectionIds) {
                    $q->where('teacher_id', (int) $user->id);
                    if ($relaxedSectionIds !== []) {
                        $q->orWhereIn('class_section_id', $relaxedSectionIds);
                    }
                });
            }
        } elseif (! empty($filters['teacher_id'])) {
            $query->where('teacher_id', (int) $filters['teacher_id']);
        }

        if (!empty($filters['class_section_id'])) {
            $query->where('class_section_id', (int) $filters['class_section_id']);
        }

        if (!empty($filters['subject'])) {
            $query->where('subject', (string) $filters['subject']);
        }

        if (!empty($filters['status'])) {
            $query->where('status', (string) $filters['status']);
        }

        if (!empty($filters['school_year']) && Schema::hasColumn('class_progress_reports', 'school_year')) {
            $query->where('school_year', (string) $filters['school_year']);
        }

        if (!empty($filters['semester']) && Schema::hasColumn('class_progress_reports', 'semester')) {
            $query->where('semester', (string) $filters['semester']);
        }

        if (!empty($filters['week_start'])) {
            $query->whereDate('week_start', '>=', $filters['week_start']);
        }

        if (!empty($filters['week_end'])) {
            $query->whereDate('week_end', '<=', $filters['week_end']);
        }

        $perPage = (int) ($filters['per_page'] ?? 20);

        return $query->paginate($perPage);
    }

    public function getReport(User $user, int $reportId): ClassProgressReport
    {
        $report = ClassProgressReport::query()->with('classSection')->findOrFail($reportId);

        if ($this->isAdmin($user)) {
            return $report;
        }

        if ($this->viewerMayAccessReport($user, $report)) {
            return $report;
        }

        abort(404);
    }

    public function weeklyReports(User $user, ClassProgressReport $report): array
    {
        $query = ClassProgressReport::query()
            ->with('classSection')
            ->where('class_section_id', $report->class_section_id)
            ->whereDate('week_start', $report->week_start)
            ->orderBy('subject', 'asc');

        // Non-admins reach this only after policy `view` passed on the anchor report (author or teaches section).

        $rows = $query->get();
        $attachments = $this->attachmentMap($rows->pluck('id')->all());

        return $rows->map(function (ClassProgressReport $row) use ($attachments) {
            $row->setAttribute('status_label', $this->statusLabel($row->status));
            $row->setAttribute('attachments', $attachments[$row->id] ?? []);
            return $row;
        })->all();
    }

    public function attachmentMap(array $reportIds): array
    {
        $reportIds = array_values(array_filter(array_map('intval', $reportIds)));
        if ($reportIds === []) {
            return [];
        }

        $rows = ClassProgressAttachment::query()
            ->whereIn('report_id', $reportIds)
            ->orderBy('id', 'asc')
            ->get();

        $map = [];
        foreach ($rows as $row) {
            $map[$row->report_id][] = [
                'id' => $row->id,
                'name' => $row->original_name ?: basename((string) $row->file_path),
                'file_path' => $row->file_path,
            ];
        }

        return $map;
    }

    public function teacherAssignments(User $user, ?string $schoolYear, ?string $semester): array
    {
        if ($schoolYear === null || $schoolYear === '') {
            $schoolYear = (string) (config('school.school_year') ?? '');
        }

        return TeacherClass::getClassAssignmentsByUserId($user->id, $schoolYear, $semester);
    }

    public function meta(?string $schoolYear = null, ?string $semester = null): array
    {
        return $this->schoolYears->options($schoolYear, $semester);
    }

    private function statusLabel(?string $status): string
    {
        $options = (array) config('progress.status_options', []);
        return $options[$status] ?? 'Unknown';
    }

    private function isAdmin(User $user): bool
    {
        $roles = $user->roles()
            ->pluck('roles.name')
            ->map(fn ($name) => strtolower((string) $name))
            ->toArray();

        foreach (['administrator', 'admin', 'principal', 'vice_principal', 'vice-principal'] as $role) {
            if (in_array($role, $roles, true)) {
                return true;
            }
        }

        return false;
    }

    /**
     * legacy `ClassProgressController::resolveAssignedTeacherIds`.
     *
     * @return list<int>
     */
    public function resolveAssignedTeacherIds(int $classSectionId): array
    {
        $schoolYear = (string) (Configuration::getConfig('school_year') ?? '');
        if ($schoolYear === '' || $classSectionId <= 0) {
            return [];
        }

        $semester = (string) (Configuration::getConfig('semester') ?? '');
        $rows = TeacherClass::assignedForSectionTerm($classSectionId, $semester, $schoolYear);
        if ($rows === []) {
            $rows = TeacherClass::assignedForSectionTerm($classSectionId, $semester, $schoolYear, null, false);
        }
        $ids = [];
        foreach ($rows as $row) {
            $id = (int) ($row['teacher_id'] ?? 0);
            if ($id > 0) {
                $ids[$id] = true;
            }
        }

        return array_map('intval', array_keys($ids));
    }

    private function viewerMayAccessReport(User $user, ClassProgressReport $report): bool
    {
        if ((int) $report->teacher_id === (int) $user->id) {
            return true;
        }

        return in_array(
            (int) $report->class_section_id,
            TeacherClass::distinctSectionIdsForTeacher((int) $user->id),
            true
        );
    }
}
