<?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.

namespace local_kid\output;

use local_kid\local\access_helper;
use local_kid\local\comparateur_repository;
use local_kid\local\competency_repository;
use local_kid\local\dashboard_favori_repository;
use local_kid\local\niveau_repository;
use local_kid\local\promotion_repository;

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

/**
 * Comparateur de bilans — cross-domain extraction/visualisation screen.
 * No promotion dropdown of its own: the "promotion affichée" is the
 * plugin-wide global switcher already built into every screen via
 * {@see \local_kid\output\renderer::render_shell()} — duplicating it here
 * would diverge from every other local_kid page.
 */
class comparateur_page implements \renderable, \templatable {

    private array $filters;
    private int $userid;

    /**
     * @param array $filters {promotionid, studentids[], sourcekeys[],
     *   competencyids[], niveaucodes[], onlyobserved, showcomments}
     */
    public function __construct(array $filters, int $userid) {
        $this->filters = $filters;
        $this->userid = $userid;
    }

    public function export_for_template(\renderer_base $output): array {
        $promotionid = (int) $this->filters['promotionid'];
        $promotion = $promotionid ? promotion_repository::get($promotionid) : null;

        if (!$promotion) {
            return [
                'hasspromotion' => false,
                'message' => get_string('no_promotion_yet', 'local_kid'),
            ];
        }

        $students = promotion_repository::get_active_students($promotionid);
        $sources = comparateur_repository::get_sources($promotionid);
        $competencies = competency_repository::get_active_for_promotion($promotionid);
        $niveaux = niveau_repository::get_all();

        $studentids = array_map('intval', $this->filters['studentids'] ?? []);
        $sourcekeys = $this->filters['sourcekeys'] ?? [];
        $competencyids = array_map('intval', $this->filters['competencyids'] ?? []);
        $niveaucodes = $this->filters['niveaucodes'] ?? [];

        $canmanagefavoris = access_helper::has_capability_or_teacher(
            'local/kid:managedashboardfavourites', \context_system::instance()
        );

        return array_merge([
            'hasspromotion' => true,
            'filterform' => [
                'action' => (new \moodle_url('/local/kid/pages/dashboard.php', ['tab' => 'comparateur']))->out(false),
                'promotionid' => $promotionid,
                'studentoptions' => $this->build_student_options($students, $studentids),
                'sourcegroups' => $this->build_source_groups($sources, $sourcekeys),
                'competencyoptions' => $this->build_competency_options($competencies, $competencyids),
                'levelchips' => $this->build_level_chips($niveaux, $niveaucodes),
                'showcomments' => !empty($this->filters['showcomments']),
                'onlyobserved' => !empty($this->filters['onlyobserved']),
            ],
            'canmanagefavoris' => $canmanagefavoris,
            'favoris' => $this->build_favoris_data(),
            'sesskey' => sesskey(),
        ], $this->export_results_for_template($students, $sources, $competencies));
    }

    /**
     * Builds just the results area's data (toolbar + student cards) —
     * everything the {@see \local_kid\external\get_comparateur_results} AJAX
     * endpoint needs to re-render {@see templates/comparateur_results.mustache}
     * on a filter change, factored out so the full-page render above and the
     * live-refresh fragment always stay in sync (single source of truth).
     *
     * @param \stdClass[] $students promotion_repository::get_active_students()
     * @param \stdClass[] $sources comparateur_repository::get_sources()
     * @param \stdClass[] $competencies competency_repository::get_active_for_promotion()
     */
    public function export_results_for_template(array $students, array $sources, array $competencies): array {
        $promotionid = (int) $this->filters['promotionid'];
        $studentids = array_map('intval', $this->filters['studentids'] ?? []);
        $sourcekeys = $this->filters['sourcekeys'] ?? [];
        $competencyids = array_map('intval', $this->filters['competencyids'] ?? []);
        $niveaucodes = $this->filters['niveaucodes'] ?? [];
        $onlyobserved = !empty($this->filters['onlyobserved']);
        $showcomments = !empty($this->filters['showcomments']);

        $canexport = access_helper::has_capability_or_teacher('local/kid:view', \context_system::instance());

        $hasselection = !empty($studentids) && !empty($sourcekeys);
        $studentcards = [];
        $totalcells = 0;

        if ($hasselection) {
            $flatfilters = [
                'promotionid' => $promotionid,
                'studentids' => $studentids,
                'sourcekeys' => $sourcekeys,
                'competencyids' => $competencyids,
                'niveaucodes' => $niveaucodes,
                'onlyobserved' => $onlyobserved,
            ];
            $cells = comparateur_repository::flatten($flatfilters);

            $bystudent = [];
            foreach ($cells as $cell) {
                $key = comparateur_repository::source_key($cell->sourcetype, $cell->sourceid);
                $bystudent[$cell->studentid][$cell->competencyid][$key] = $cell;
            }

            $selectedsources = array_values(array_filter(
                $sources, fn($s) => in_array(comparateur_repository::source_key($s->type, $s->id), $sourcekeys, true)
            ));
            $competenciesbyid = [];
            foreach ($competencies as $competency) {
                $competenciesbyid[(int) $competency->id] = $competency;
            }
            $studentsbyid = [];
            foreach ($students as $student) {
                $studentsbyid[(int) $student->studentid] = $student;
            }

            foreach ($studentids as $studentid) {
                if (!isset($studentsbyid[$studentid])) {
                    continue; // Not an active student of this promotion.
                }
                $studentcards[] = $this->build_student_card(
                    $studentsbyid[$studentid], $bystudent[$studentid] ?? [],
                    $selectedsources, $competenciesbyid, $showcomments
                );
                $totalcells += count($bystudent[$studentid] ?? []);
            }
        }

        return [
            'hasselection' => $hasselection,
            'emptymessage' => get_string('comparateur_empty', 'local_kid'),
            'studentcards' => $studentcards,
            'hasstudentcards' => !empty($studentcards),
            'stats' => $hasselection
                ? get_string('comparateur_stats', 'local_kid', (object) [
                    'students' => count($studentcards),
                    'sources' => count($sourcekeys),
                    'cells' => $totalcells,
                ])
                : get_string('comparateur_nostats', 'local_kid'),
            'canexport' => $canexport && $hasselection,
            'csvurl' => $this->build_export_url('csv'),
            'xlsxurl' => $this->build_export_url('xlsx'),
        ];
    }

    private function build_student_card(
        \stdClass $student, array $competencycells, array $selectedsources,
        array $competenciesbyid, bool $showcomments
    ): array {
        $rowcompetencyids = array_keys($competencycells);
        usort($rowcompetencyids, fn($a, $b) =>
            ($competenciesbyid[$a]->sortorder ?? 0) <=> ($competenciesbyid[$b]->sortorder ?? 0)
        );

        $groups = [];
        foreach ($selectedsources as $source) {
            $last = end($groups);
            if ($last !== false && $last['type'] === $source->type) {
                $groups[count($groups) - 1]['span']++;
            } else {
                $groups[] = ['type' => $source->type, 'label' => $this->type_group_label($source->type), 'span' => 1];
            }
        }

        $sourceheaders = [];
        foreach ($selectedsources as $source) {
            $sourceheaders[] = [
                'label' => $source->label,
                'url' => $this->saisie_url($source, (int) $student->studentid)->out(false),
            ];
        }

        $rows = [];
        foreach ($rowcompetencyids as $competencyid) {
            $competency = $competenciesbyid[$competencyid];
            $rowcells = [];
            foreach ($selectedsources as $source) {
                $key = comparateur_repository::source_key($source->type, $source->id);
                $cell = $competencycells[$competencyid][$key] ?? null;
                if (!$cell) {
                    $rowcells[] = ['outofscope' => true];
                    continue;
                }
                $comment = trim((string) $cell->commentaire);
                $long = mb_strlen($comment) > 120;
                $rowcells[] = [
                    'outofscope' => false,
                    'niveaulabel' => $cell->niveaulabel,
                    'niveauclass' => $cell->niveauclass,
                    'hascomment' => $showcomments && $comment !== '',
                    'commentshort' => $long ? mb_substr($comment, 0, 120) . '…' : $comment,
                    'commentfull' => $comment,
                    'commentislong' => $long,
                    'saisieurl' => $this->saisie_url($source, (int) $student->studentid)->out(false),
                    'sourcelabel' => $source->label,
                    'sourcetypelabel' => $this->type_one_label($source->type),
                ];
            }
            $rows[] = [
                'competencycode' => $competency->codecourt,
                'competencylabel' => competency_repository::label($competency),
                'studentname' => fullname($student),
                'cells' => $rowcells,
            ];
        }

        return [
            'studentname' => fullname($student),
            'cardstats' => get_string('comparateur_card_stats', 'local_kid', (object) [
                'competencies' => count($rows),
                'sources' => count($selectedsources),
            ]),
            'groupheaders' => $groups,
            'sourceheaders' => $sourceheaders,
            'rows' => $rows,
            'hasrows' => !empty($rows),
        ];
    }

    private function saisie_url(\stdClass $source, int $studentid): \moodle_url {
        return match ($source->type) {
            'eval' => new \moodle_url('/local/kid/pages/evaluations.php', ['evalid' => $source->id, 'studentid' => $studentid]),
            'stage' => new \moodle_url('/local/kid/pages/stages.php', ['stageid' => $source->id, 'studentid' => $studentid]),
            'capp' => new \moodle_url('/local/kid/pages/capp.php', ['sessionid' => $source->id, 'studentid' => $studentid]),
        };
    }

    private function type_group_label(string $type): string {
        return match ($type) {
            'eval' => get_string('nav_evaluations', 'local_kid'),
            'stage' => get_string('nav_stages', 'local_kid'),
            'capp' => get_string('nav_capp', 'local_kid'),
        };
    }

    private function type_one_label(string $type): string {
        return match ($type) {
            'eval' => get_string('comparateur_type_eval', 'local_kid'),
            'stage' => get_string('comparateur_type_stage', 'local_kid'),
            'capp' => get_string('comparateur_type_capp', 'local_kid'),
        };
    }

    private function build_student_options(array $students, array $selected): array {
        $options = [];
        foreach ($students as $student) {
            $options[] = [
                'id' => (int) $student->studentid,
                'label' => fullname($student),
                'checked' => in_array((int) $student->studentid, $selected, true),
            ];
        }
        return $options;
    }

    private function build_source_groups(array $sources, array $selectedkeys): array {
        $bytype = ['eval' => [], 'stage' => [], 'capp' => []];
        foreach ($sources as $source) {
            $bytype[$source->type][] = $source;
        }
        $groups = [];
        foreach ($bytype as $type => $list) {
            if (!$list) {
                continue;
            }
            $options = [];
            foreach ($list as $source) {
                $key = comparateur_repository::source_key($source->type, $source->id);
                $options[] = ['key' => $key, 'label' => $source->label, 'checked' => in_array($key, $selectedkeys, true)];
            }
            $groups[] = ['label' => $this->type_group_label($type), 'options' => $options];
        }
        return $groups;
    }

    private function build_competency_options(array $competencies, array $selectedids): array {
        $options = [];
        foreach ($competencies as $competency) {
            $options[] = [
                'id' => (int) $competency->id,
                'label' => competency_repository::label($competency, true),
                'checked' => in_array((int) $competency->id, $selectedids, true),
            ];
        }
        return $options;
    }

    private function build_level_chips(array $niveaux, array $selectedcodes): array {
        $chips = [];
        foreach ($niveaux as $niveau) {
            $chips[] = [
                'code' => $niveau->code,
                'label' => format_string($niveau->libelle),
                'pressed' => in_array($niveau->code, $selectedcodes, true),
            ];
        }
        return $chips;
    }

    private function build_favoris_data(): array {
        $favoris = dashboard_favori_repository::get_for_user($this->userid);
        $out = [];
        foreach ($favoris as $favori) {
            $out[] = [
                'id' => (int) $favori->id,
                'name' => format_string($favori->name),
                'configjson' => $favori->config, // Already-escaped-safe JSON, only ever injected via a data-* attribute.
            ];
        }
        return $out;
    }

    /**
     * moodle_url rejects array-valued params outright ("Url parameters
     * values can not be arrays!") — build the querystring by hand instead,
     * scalars via moodle_url as usual, array filters appended manually as
     * repeated key[]=value pairs (PHP's own GET-array convention, read back
     * by optional_param_array() in comparateur_export.php).
     */
    private function build_export_url(string $format): string {
        $baseurl = new \moodle_url('/local/kid/pages/comparateur_export.php', array_filter([
            'format' => $format,
            'promotionid' => $this->filters['promotionid'] ?? 0,
            'onlyobserved' => !empty($this->filters['onlyobserved']) ? 1 : 0,
        ]));

        $arrayparams = [];
        foreach (['studentids', 'sourcekeys', 'competencyids', 'niveaucodes'] as $key) {
            foreach ($this->filters[$key] ?? [] as $value) {
                $arrayparams[] = $key . '[]=' . urlencode((string) $value);
            }
        }

        $url = $baseurl->out(false);
        if ($arrayparams) {
            $url .= (str_contains($url, '?') ? '&' : '?') . implode('&', $arrayparams);
        }
        return $url;
    }
}
