<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

/**
 * Builds the single DTO consumed by both docx_service and pdf_service (and
 * by the "Vue élève" screen itself), so the document formats and the HTML
 * page can never structurally diverge.
 *
 * @package    local_kid
 */

namespace local_kid\local;

defined('MOODLE_INTERNAL') || die();

class fiche_builder {

    /**
     * @param int $studentid local_kid_student.id — same id space as every
     *        other domain repository's *_entry.studentid, never a Moodle
     *        userid directly (see promotion_repository::get_active_student_record()
     *        for the userid → studentid bridge used by the "Vue élève" screen).
     * @param bool $publishedonly when true (student's own download, or a
     *        staff "preview as student"), only published dossiers are
     *        included and every item carries no workflow/publication
     *        detail beyond the fact that it is published. When false (a
     *        staff working download from the Évaluations/Stages/CAPP
     *        screens), every active dossier is included regardless of
     *        publication state, each one labelled with its real workflow
     *        status, so staff can tell an internal working copy from a
     *        document the student has actually received.
     */
    public static function build(int $studentid, int $promotionid, bool $publishedonly): \stdClass {
        global $DB;

        $studentrecord = $DB->get_record('local_kid_student', ['id' => $studentid], '*', MUST_EXIST);
        $user = $DB->get_record('user', ['id' => $studentrecord->userid], '*', MUST_EXIST);
        $promotion = promotion_repository::get($promotionid);

        $competencies = [];
        foreach (competency_repository::get_for_promotion($promotionid) as $competency) {
            $competencies[$competency->id] = $competency;
        }

        $niveaux = [];
        foreach (niveau_repository::get_all() as $niveau) {
            $niveaux[$niveau->id] = $niveau;
        }

        return (object) [
            'student' => (object) [
                'fullname' => fullname($user),
                'promotionname' => format_string($promotion->name),
                'codeena' => self::resolve_codeena((int) $studentrecord->userid),
            ],
            'evaluations' => self::build_eval_items($studentid, $promotionid, $publishedonly, $competencies, $niveaux),
            'stages' => self::build_stage_items($studentid, $promotionid, $publishedonly, $niveaux),
            'capp' => self::build_capp_items($studentid, $promotionid, $publishedonly, $competencies, $niveaux),
            'generatedat' => time(),
        ];
    }

    private static function build_eval_items(int $studentid, int $promotionid, bool $publishedonly, array $competencies, array $niveaux): array {
        $items = [];
        foreach (eval_repository::get_for_promotion($promotionid) as $eval) {
            if (!$eval->active) {
                continue;
            }
            $entry = eval_repository::get_entry_for_student((int) $eval->id, $studentid);
            if (!$entry || ($publishedonly && !$entry->published)) {
                continue;
            }
            $results = eval_repository::get_results((int) $entry->id);
            $competencyids = array_keys(eval_repository::get_competencies((int) $eval->id));
            $moodlefeedback = ($eval->sourcetype === 'moodle_devoir' && !empty($entry->feedbacktext))
                ? trim(strip_tags($entry->feedbacktext))
                : null;
            $items[] = (object) [
                'id' => (int) $eval->id,
                'titre' => format_string($eval->titre),
                'date' => $eval->evaldate ? userdate($eval->evaldate, get_string('strftimedatefullshort', 'core_langconfig')) : null,
                'workflowstatus' => $entry->workflowstatus,
                'statuslabel' => get_string('workflow_' . $entry->workflowstatus, 'local_kid'),
                'published' => (bool) $entry->published,
                'competences' => self::build_competency_cells($competencyids, $results, $competencies, $niveaux, $moodlefeedback),
            ];
        }
        return $items;
    }

    private static function build_stage_items(int $studentid, int $promotionid, bool $publishedonly, array $niveaux): array {
        $items = [];
        foreach (stage_repository::get_for_promotion($promotionid) as $stage) {
            if (!$stage->active) {
                continue;
            }
            $entry = stage_repository::get_entry_for_student((int) $stage->id, $studentid);
            if (!$entry || ($publishedonly && !$entry->published)) {
                continue;
            }
            $results = stage_repository::get_results((int) $entry->id);
            $scopecompetencies = [];
            foreach (stage_repository::get_scope_competencies($stage) as $competency) {
                $scopecompetencies[$competency->id] = $competency;
            }
            $items[] = (object) [
                'id' => (int) $stage->id,
                'titre' => format_string($stage->libelle),
                'date' => null,
                'workflowstatus' => $entry->workflowstatus,
                'statuslabel' => get_string('workflow_' . $entry->workflowstatus, 'local_kid'),
                'published' => (bool) $entry->published,
                'commentaireglobal' => $entry->commentaireglobal,
                'competences' => self::build_competency_cells(array_keys($scopecompetencies), $results, $scopecompetencies, $niveaux),
            ];
        }
        return $items;
    }

    private static function build_capp_items(int $studentid, int $promotionid, bool $publishedonly, array $competencies, array $niveaux): array {
        $items = [];
        foreach (capp_repository::get_sessions_for_promotion($promotionid) as $session) {
            if (!$session->active) {
                continue;
            }
            $entry = capp_repository::get_entry_for_student((int) $session->id, $studentid);
            if (!$entry || ($publishedonly && !$entry->published)) {
                continue;
            }
            $results = capp_repository::get_results((int) $entry->id);
            $items[] = (object) [
                'id' => (int) $session->id,
                'titre' => format_string($session->code . ' — ' . $session->libelle),
                'date' => null,
                'workflowstatus' => $entry->workflowstatus,
                'statuslabel' => get_string('workflow_' . $entry->workflowstatus, 'local_kid'),
                'published' => (bool) $entry->published,
                'appreciationglobale' => $entry->appreciationglobale,
                'competences' => self::build_competency_cells(array_keys($competencies), $results, $competencies, $niveaux),
            ];
        }
        return $items;
    }

    /**
     * @param int[] $competencyids
     * @param \stdClass[] $results keyed by competencyid (niveauid/commentaire)
     * @param \stdClass[] $competencies keyed by id
     * @param \stdClass[] $niveaux keyed by id
     * @param ?string $moodlefeedback read-only Moodle assignment feedback,
     *        single value per (évaluation, élève) — never per-competency
     *        storage, just repeated against every competency this eval is
     *        linked to (local_kid_eval_competency) so it stays "en regard"
     *        of its competency the way the manual commentaire does. Only
     *        ever non-null for moodle_devoir evals; always null for
     *        stage/CAPP cells, which never pass this argument.
     */
    private static function build_competency_cells(
        array $competencyids,
        array $results,
        array $competencies,
        array $niveaux,
        ?string $moodlefeedback = null
    ): array {
        $cells = [];
        foreach ($competencyids as $competencyid) {
            $competency = $competencies[$competencyid] ?? null;
            if (!$competency) {
                continue;
            }
            $result = $results[$competencyid] ?? null;
            $niveauid = $result->niveauid ?? null;
            $niveau = ($niveauid && isset($niveaux[$niveauid])) ? $niveaux[$niveauid] : null;
            $cells[] = (object) [
                'code' => $competency->codecourt,
                'libelle' => competency_repository::label($competency),
                'niveau' => $niveau->libelle ?? null,
                // Screen-only field (used by student_page.php for the niveau
                // badge via niveau_repository::badge_class()) — the DOCX/PDF
                // exporters don't read this property, they only use 'niveau'.
                'niveaucode' => $niveau->code ?? null,
                'commentaire' => $result->commentaire ?? null,
                'moodlefeedback' => $moodlefeedback,
                'hasmoodlefeedback' => $moodlefeedback !== null && $moodlefeedback !== '',
            ];
        }
        return $cells;
    }

    /**
     * Never blocks fiche generation if nothing resolves — unlike the ADESI
     * export (adesi_export_service), which does require a code_ena.
     */
    private static function resolve_codeena(int $userid): ?string {
        return codeena_resolver::resolve($userid);
    }
}
