<?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\competency_repository;
use local_kid\local\journal_repository;
use local_kid\local\niveau_repository;
use local_kid\local\promotion_repository;

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

class journal_page implements \renderable, \templatable {

    private const PERPAGE = 30;
    private const DOMAINS = ['eval', 'stage', 'capp'];

    private array $filters;
    private string $sort;
    private string $dir;
    private int $page;

    /** @var array<int, string> competencyid => label, lazily filled, avoids re-fetching the same competency across rows. */
    private array $competencylabels = [];

    /** @var array<int, string> niveauid => label, same rationale. */
    private array $niveaulabels = [];

    /**
     * @param array $filters raw GET values, already PARAM_*-cleaned by the controller:
     *   userid, promotionid (int, 0 = all), domain, action (string, '' = all),
     *   studentquery (string), datefrom, dateto (int timestamps, 0 = unbounded)
     */
    public function __construct(array $filters, string $sort, string $dir, int $page) {
        $this->filters = $filters;
        $this->sort = $sort;
        $this->dir = $dir;
        $this->page = max(0, $page);
    }

    public function export_for_template(\renderer_base $output): array {
        $result = journal_repository::get_filtered($this->filters, $this->sort, $this->dir, $this->page, self::PERPAGE);

        $rows = array_map([$this, 'export_row'], $result['rows']);

        $baseurl = new \moodle_url('/local/kid/pages/admin.php', array_filter([
            'tab' => 'journal',
            'userid' => $this->filters['userid'] ?? 0,
            'promotionid' => $this->filters['promotionid'] ?? 0,
            'domain' => $this->filters['domain'] ?? '',
            'action' => $this->filters['action'] ?? '',
            'studentquery' => $this->filters['studentquery'] ?? '',
            'datefrom' => $this->filters['datefrom'] ?? '',
            'dateto' => $this->filters['dateto'] ?? '',
            'sort' => $this->sort,
            'dir' => $this->dir,
        ]));

        return [
            'filterform' => [
                'action' => (new \moodle_url('/local/kid/pages/admin.php', ['tab' => 'journal']))->out(false),
                'authoroptions' => $this->build_options(journal_repository::get_distinct_authors(), (int) ($this->filters['userid'] ?? 0)),
                'promotionoptions' => $this->build_promotion_options(),
                'domainoptions' => $this->build_domain_options(),
                'actionoptions' => $this->build_action_options(),
                'studentquery' => $this->filters['studentquery'] ?? '',
                'datefrom' => !empty($this->filters['datefrom']) ? userdate($this->filters['datefrom'], '%Y-%m-%d') : '',
                'dateto' => !empty($this->filters['dateto']) ? userdate($this->filters['dateto'], '%Y-%m-%d') : '',
                'sort' => $this->sort,
                'dir' => $this->dir,
            ],
            'sortheaders' => $this->build_sort_headers($baseurl),
            'hasrows' => !empty($rows),
            'message' => get_string('journal_empty', 'local_kid'),
            'rows' => $rows,
            'pagingbar' => $output->render(new \paging_bar($result['total'], $this->page, self::PERPAGE, $baseurl, 'page')),
            'total' => $result['total'],
        ];
    }

    private function export_row(\stdClass $row): array {
        $author = (object) ['firstname' => $row->authorfirstname, 'lastname' => $row->authorlastname];
        $studentname = $row->studentid
            ? fullname((object) ['firstname' => $row->studentfirstname, 'lastname' => $row->studentlastname])
            : '';

        $context = null;
        if (!empty($row->context)) {
            $decoded = json_decode($row->context, true);
            if (is_array($decoded)) {
                $pairs = [];
                foreach ($decoded as $key => $value) {
                    if (in_array($key, ['domain', 'entryid', 'studentid', 'promotionid'], true)) {
                        continue; // Déjà affichés dans leurs propres colonnes.
                    }
                    $pairs[] = s($key) . ' : ' . s((string) $value);
                }
                $context = implode(', ', $pairs);
            }
        }

        $diffhtml = $this->build_diff_html($row->oldvalue, $row->newvalue);

        return [
            'id' => (int) $row->id,
            'timecreated' => userdate($row->timecreated, get_string('strftimedatetimeshort', 'core_langconfig')),
            'author' => fullname($author),
            'authorurl' => (new \moodle_url('/user/profile.php', ['id' => (int) $row->userid]))->out(false),
            'actionlabel' => $this->action_label($row->action),
            'domainlabel' => $row->domain ? get_string('nav_' . $this->domain_to_nav($row->domain), 'local_kid') : '',
            'promotionname' => $row->promotionname ? format_string($row->promotionname) : '',
            'studentname' => $studentname,
            'studenturl' => $row->studentid
                ? (new \moodle_url('/local/kid/pages/student.php', ['studentid' => (int) $row->studentid]))->out(false)
                : '',
            'context' => $context,
            'haschanges' => $diffhtml !== '',
            'diffhtml' => $diffhtml,
        ];
    }

    /**
     * Decodes the competencyid => {niveauid, commentaire} JSON blobs stored
     * in `local_kid_journal.oldvalue`/`newvalue` (see journal_service::
     * split_diff()) into pre-escaped, human-readable HTML — competency and
     * niveau **labels**, never raw JSON, ids, or codes on screen.
     */
    private function build_diff_html(?string $oldvalue, ?string $newvalue): string {
        $old = $oldvalue ? json_decode($oldvalue, true) : null;
        $new = $newvalue ? json_decode($newvalue, true) : null;
        if (!is_array($old) && !is_array($new)) {
            return '';
        }
        $old = $old ?: [];
        $new = $new ?: [];

        $lines = '';
        foreach (array_keys($old + $new) as $competencyid) {
            $lines .= \html_writer::tag('div',
                \html_writer::tag('strong', s($this->competency_label((int) $competencyid))) . ' : ' .
                \html_writer::tag('del', s($this->format_result_value($old[$competencyid] ?? null))) . ' → ' .
                \html_writer::tag('ins', s($this->format_result_value($new[$competencyid] ?? null))),
                ['class' => 'kid-journal__diffline']
            );
        }
        return $lines;
    }

    /** @param ?array{niveauid: ?int, commentaire: ?string} $value */
    private function format_result_value(?array $value): string {
        if (!$value) {
            return get_string('journal_diff_empty', 'local_kid');
        }
        $niveau = !empty($value['niveauid'])
            ? $this->niveau_label((int) $value['niveauid'])
            : get_string('journal_diff_nolevel', 'local_kid');
        $comment = trim((string) ($value['commentaire'] ?? ''));
        return $comment !== '' ? $niveau . ' — ' . $comment : $niveau;
    }

    private function competency_label(int $competencyid): string {
        if (!array_key_exists($competencyid, $this->competencylabels)) {
            $competency = competency_repository::get($competencyid);
            $this->competencylabels[$competencyid] = $competency
                ? competency_repository::label($competency, true)
                : get_string('journal_diff_unknown', 'local_kid');
        }
        return $this->competencylabels[$competencyid];
    }

    private function niveau_label(int $niveauid): string {
        if (!array_key_exists($niveauid, $this->niveaulabels)) {
            $niveau = niveau_repository::get($niveauid);
            $this->niveaulabels[$niveauid] = $niveau ? format_string($niveau->libelle) : get_string('journal_diff_unknown', 'local_kid');
        }
        return $this->niveaulabels[$niveauid];
    }

    private function domain_to_nav(string $domain): string {
        return ['eval' => 'evaluations', 'stage' => 'stages', 'capp' => 'capp'][$domain] ?? $domain;
    }

    private function action_label(string $action): string {
        $stringid = 'event_' . $action;
        return get_string_manager()->string_exists($stringid, 'local_kid') ? get_string($stringid, 'local_kid') : $action;
    }

    private function build_options(array $idtolabel, int $selected): array {
        $options = [];
        foreach ($idtolabel as $id => $label) {
            $options[] = ['id' => $id, 'label' => $label, 'selected' => $id === $selected];
        }
        return $options;
    }

    private function build_promotion_options(): array {
        $selected = (int) ($this->filters['promotionid'] ?? 0);
        $options = [];
        foreach (promotion_repository::get_all() as $promotion) {
            $options[] = ['id' => (int) $promotion->id, 'label' => format_string($promotion->name), 'selected' => (int) $promotion->id === $selected];
        }
        return $options;
    }

    private function build_domain_options(): array {
        $selected = $this->filters['domain'] ?? '';
        $options = [];
        foreach (self::DOMAINS as $domain) {
            $options[] = ['id' => $domain, 'label' => get_string('nav_' . $this->domain_to_nav($domain), 'local_kid'), 'selected' => $domain === $selected];
        }
        return $options;
    }

    private function build_action_options(): array {
        $selected = $this->filters['action'] ?? '';
        $options = [];
        foreach (journal_repository::get_distinct_actions() as $action) {
            $options[] = ['id' => $action, 'label' => $this->action_label($action), 'selected' => $action === $selected];
        }
        return $options;
    }

    private function build_sort_headers(\moodle_url $baseurl): array {
        $columns = [
            'timecreated' => 'journal_col_date',
            'userid' => 'journal_col_author',
            'domain' => 'journal_col_domain',
            'action' => 'journal_col_action',
        ];

        $headers = [];
        foreach ($columns as $field => $stringid) {
            $isactive = $this->sort === $field;
            $nextdir = $isactive && $this->dir === 'ASC' ? 'DESC' : 'ASC';
            $url = new \moodle_url($baseurl, ['sort' => $field, 'dir' => $nextdir]);
            $headers[$field] = [
                'label' => get_string($stringid, 'local_kid'),
                'url' => $url->out(false),
                'indicator' => $isactive ? ($this->dir === 'ASC' ? '▲' : '▼') : '',
            ];
        }
        return $headers;
    }
}
