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

/**
 * Privacy provider (section 27 du cahier des charges). Toutes les données
 * personnelles de ce plugin vivent au contexte système (aucune donnée de
 * cours) : un élève y a une ligne `local_kid_student` par promotion à
 * laquelle il a appartenu, avec ses dossiers éval/stage/CAPP associés.
 *
 * Suppression : documentée comme non implémentée (retournée sans effet)
 * plutôt que silencieusement ignorée — les dossiers d'évaluation par
 * compétences relèvent d'obligations de conservation institutionnelles
 * (traçabilité pédagogique), même limitation assumée que pour le
 * backup/restore (voir README.md).
 *
 * @package local_kid
 */

namespace local_kid\privacy;

use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;

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

class provider implements
    \core_privacy\local\metadata\provider,
    \core_privacy\local\request\core_userlist_provider,
    \core_privacy\local\request\plugin\provider {

    public static function get_metadata(collection $collection): collection {
        $collection->add_database_table('local_kid_student', [
            'userid' => 'privacy:metadata:local_kid_student:userid',
            'promotionid' => 'privacy:metadata:local_kid_student:promotionid',
            'status' => 'privacy:metadata:local_kid_student:status',
        ], 'privacy:metadata:local_kid_student');

        $collection->add_database_table('local_kid_eval_result', [
            'niveauid' => 'privacy:metadata:result:niveauid',
            'commentaire' => 'privacy:metadata:result:commentaire',
        ], 'privacy:metadata:local_kid_eval_result');

        $collection->add_database_table('local_kid_stage_result', [
            'niveauid' => 'privacy:metadata:result:niveauid',
            'commentaire' => 'privacy:metadata:result:commentaire',
        ], 'privacy:metadata:local_kid_stage_result');

        $collection->add_database_table('local_kid_stage_entry', [
            'commentaireglobal' => 'privacy:metadata:local_kid_stage_entry:commentaireglobal',
        ], 'privacy:metadata:local_kid_stage_entry');

        $collection->add_database_table('local_kid_stage_doc', [
            'filename' => 'privacy:metadata:local_kid_stage_doc:filename',
            'timecreated' => 'privacy:metadata:local_kid_stage_doc:timecreated',
        ], 'privacy:metadata:local_kid_stage_doc');

        $collection->add_database_table('local_kid_capp_result', [
            'niveauid' => 'privacy:metadata:result:niveauid',
            'commentaire' => 'privacy:metadata:result:commentaire',
        ], 'privacy:metadata:local_kid_capp_result');

        $collection->add_database_table('local_kid_capp_entry', [
            'appreciationglobale' => 'privacy:metadata:local_kid_capp_entry:appreciationglobale',
        ], 'privacy:metadata:local_kid_capp_entry');

        $collection->add_database_table('local_kid_codeena_map', [
            'userid' => 'privacy:metadata:local_kid_codeena_map:userid',
            'codeena' => 'privacy:metadata:local_kid_codeena_map:codeena',
        ], 'privacy:metadata:local_kid_codeena_map');

        $collection->add_database_table('local_kid_journal', [
            'userid' => 'privacy:metadata:local_kid_journal:userid',
            'action' => 'privacy:metadata:local_kid_journal:action',
            'studentid' => 'privacy:metadata:local_kid_journal:studentid',
            'oldvalue' => 'privacy:metadata:local_kid_journal:oldvalue',
            'newvalue' => 'privacy:metadata:local_kid_journal:newvalue',
            'context' => 'privacy:metadata:local_kid_journal:context',
        ], 'privacy:metadata:local_kid_journal');

        $collection->add_external_location_link('adesi', [
            'codeena' => 'privacy:metadata:adesi:codeena',
            'niveau_competence' => 'privacy:metadata:adesi:niveau_competence',
            'commentaire' => 'privacy:metadata:adesi:commentaire',
        ], 'privacy:metadata:adesi');

        return $collection;
    }

    public static function get_contexts_for_userid(int $userid): contextlist {
        global $DB;
        $contextlist = new contextlist();
        if ($DB->record_exists('local_kid_student', ['userid' => $userid])
                || $DB->record_exists('local_kid_codeena_map', ['userid' => $userid])
                || $DB->record_exists('local_kid_journal', ['userid' => $userid])) {
            $contextlist->add_system_context();
        }
        return $contextlist;
    }

    public static function get_users_in_context(userlist $userlist): void {
        if ($userlist->get_context()->contextlevel !== CONTEXT_SYSTEM) {
            return;
        }
        $userlist->add_from_sql('userid', 'SELECT DISTINCT userid FROM {local_kid_student}', []);
        $userlist->add_from_sql('userid', 'SELECT DISTINCT userid FROM {local_kid_codeena_map}', []);
        $userlist->add_from_sql('userid', 'SELECT DISTINCT userid FROM {local_kid_journal}', []);
    }

    public static function export_user_data(approved_contextlist $contextlist): void {
        global $DB;

        $hassystem = false;
        foreach ($contextlist->get_contexts() as $context) {
            if ($context->contextlevel === CONTEXT_SYSTEM) {
                $hassystem = true;
            }
        }
        if (!$hassystem) {
            return;
        }

        $userid = (int) $contextlist->get_user()->id;
        $systemcontext = \context_system::instance();
        $path = [get_string('pluginname', 'local_kid')];

        $studentrows = $DB->get_records('local_kid_student', ['userid' => $userid]);
        $studentids = [];
        foreach ($studentrows as $student) {
            $studentids[] = (int) $student->id;

            $promotion = $DB->get_record('local_kid_promotion', ['id' => $student->promotionid], '*', IGNORE_MISSING);
            if (!$promotion) {
                continue;
            }
            $promotionpath = array_merge($path, [format_string($promotion->name)]);

            $evaldata = self::export_domain($student->id, 'local_kid_eval_entry', 'evalid', 'local_kid_eval', 'titre');
            $stagedata = self::export_domain($student->id, 'local_kid_stage_entry', 'stageid', 'local_kid_stage', 'libelle', 'commentaireglobal');
            $cappdata = self::export_domain($student->id, 'local_kid_capp_entry', 'sessionid', 'local_kid_capp_session', 'code', 'appreciationglobale');

            writer::with_context($systemcontext)->export_data($promotionpath, (object) [
                'status' => $student->status,
                'evaluations' => $evaldata,
                'stages' => $stagedata,
                'capp' => $cappdata,
            ]);
        }

        $codeena = $DB->get_record('local_kid_codeena_map', ['userid' => $userid], '*', IGNORE_MISSING);
        if ($codeena) {
            writer::with_context($systemcontext)->export_data(array_merge($path, [get_string('student_field_code', 'local_kid')]), (object) [
                'codeena' => $codeena->codeena,
                'source' => $codeena->source,
            ]);
        }

        // Journal : deux exports distincts, jamais fusionnés — les actions
        // que cet utilisateur a effectuées (auteur) ne relèvent pas du même
        // usage RGPD que les actions dont il est le sujet (élève concerné).
        $authored = $DB->get_records('local_kid_journal', ['userid' => $userid], 'timecreated ASC');
        if ($authored) {
            writer::with_context($systemcontext)->export_data(
                array_merge($path, [get_string('privacy_journal_authored', 'local_kid')]),
                (object) ['entries' => array_map([self::class, 'export_journal_row'], array_values($authored))]
            );
        }

        if ($studentids) {
            [$insql, $inparams] = $DB->get_in_or_equal($studentids);
            $concerning = $DB->get_records_select('local_kid_journal', "studentid $insql", $inparams, 'timecreated ASC');
            if ($concerning) {
                writer::with_context($systemcontext)->export_data(
                    array_merge($path, [get_string('privacy_journal_concerning', 'local_kid')]),
                    (object) ['entries' => array_map([self::class, 'export_journal_row'], array_values($concerning))]
                );
            }
        }
    }

    private static function export_journal_row(\stdClass $row): array {
        return [
            'timecreated' => userdate($row->timecreated),
            'action' => $row->action,
            'domain' => $row->domain,
            'oldvalue' => $row->oldvalue,
            'newvalue' => $row->newvalue,
            'context' => $row->context,
        ];
    }

    /**
     * @return array one entry per dossier (eval/stage/capp), each with its
     *         own niveau/commentaire results — read-only export, no
     *         side-effects.
     */
    private static function export_domain(int $studentid, string $entrytable, string $parentfk, string $parenttable, string $labelfield, ?string $globalcommentfield = null): array {
        global $DB;

        $entries = $DB->get_records($entrytable, ['studentid' => $studentid]);
        $out = [];
        foreach ($entries as $entry) {
            $parent = $DB->get_record($parenttable, ['id' => $entry->{$parentfk}], '*', IGNORE_MISSING);
            $resulttable = $entrytable === 'local_kid_eval_entry' ? 'local_kid_eval_result'
                : ($entrytable === 'local_kid_stage_entry' ? 'local_kid_stage_result' : 'local_kid_capp_result');
            $results = $DB->get_records($resulttable, ['entryid' => $entry->id]);

            $item = [
                'label' => $parent ? format_string($parent->{$labelfield}) : '',
                'workflowstatus' => $entry->workflowstatus,
                'published' => (bool) $entry->published,
                'results' => array_map(fn($r) => [
                    'competencyid' => (int) $r->competencyid,
                    'niveauid' => $r->niveauid ? (int) $r->niveauid : null,
                    'commentaire' => $r->commentaire,
                ], array_values($results)),
            ];
            if ($globalcommentfield) {
                $item[$globalcommentfield] = $entry->{$globalcommentfield};
            }
            $out[] = $item;
        }
        return $out;
    }

    /**
     * Not implemented: assessment/committee records are retained under
     * institutional pedagogical-traceability obligations, same documented
     * limitation as backup/restore (README.md) — a request handler still
     * needs to exist for Moodle's privacy API, but it intentionally does
     * not delete anything. `local_kid_journal` follows the same policy for
     * the same reason: a purgeable-on-request audit trail loses its
     * evidentiary value — its only deletion path is the automatic,
     * retention-based purge (`journal_purge_task`), never a per-user
     * privacy request.
     */
    public static function delete_data_for_all_users_in_context(\context $context): void {
    }

    public static function delete_data_for_user(approved_contextlist $contextlist): void {
    }

    public static function delete_data_for_users(approved_userlist $userlist): void {
    }
}
