<?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\local;

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

/**
 * Backs the grouped ("actions groupées") selection bar shared by the
 * Évaluations/Stages/CAPP récapitulatif screens — resolves the one string
 * "screen" identifier used everywhere else in these three screens (see
 * {@see student_nav_helper}, classes/external/get_grid_student_view.php)
 * to the domain string/table name workflow_service expects, and applies a
 * publish/hide action to a batch of studentids at once. Every eligibility
 * check here is re-verified against the database — the caller (external
 * function, bulk ZIP page controller) never trusts the browser's selection
 * beyond which studentids it asked for.
 */
class bulk_action_service {

    const SCREENS = [
        'evaluations' => [
            'domain' => 'eval',
            'table' => 'local_kid_eval_entry',
            'publishcap' => 'local/kid:publish_evaluation',
        ],
        'stages' => [
            'domain' => 'stage',
            'table' => 'local_kid_stage_entry',
            'publishcap' => 'local/kid:publish_stage',
        ],
        'capp' => [
            'domain' => 'capp',
            'table' => 'local_kid_capp_entry',
            'publishcap' => 'local/kid:publish_capp',
        ],
    ];

    public static function is_valid_screen(string $screen): bool {
        return isset(self::SCREENS[$screen]);
    }

    public static function publish_capability(string $screen): string {
        return self::SCREENS[$screen]['publishcap'];
    }

    /**
     * The eval/stage/session record for this item, whichever domain it
     * belongs to — only ->promotionid and ->id are relied on by callers.
     */
    public static function get_item(string $screen, int $itemid): ?\stdClass {
        switch ($screen) {
            case 'evaluations':
                return eval_repository::get($itemid);
            case 'stages':
                return stage_repository::get($itemid);
            case 'capp':
                return capp_repository::get_session($itemid);
            default:
                return null;
        }
    }

    /**
     * @return \stdClass[] every entry for this item, keyed by studentid —
     *         the authoritative population a submitted studentid is
     *         checked against (an id outside this set is silently dropped,
     *         never trusted from the request alone).
     */
    public static function get_entries(string $screen, int $itemid): array {
        switch ($screen) {
            case 'evaluations':
                $entries = eval_repository::get_entries_for_eval($itemid);
                break;
            case 'stages':
                $entries = stage_repository::get_entries_for_stage($itemid);
                break;
            case 'capp':
                $entries = capp_repository::get_entries_for_session($itemid);
                break;
            default:
                $entries = [];
        }
        $bystudent = [];
        foreach ($entries as $entry) {
            $bystudent[(int) $entry->studentid] = $entry;
        }
        return $bystudent;
    }

    /**
     * Applies 'publish' or 'hide' to every given studentid that actually
     * has an entry for this item. Publishing silently skips (reports as
     * "ignored") any entry not in the `finalise` workflow status — never a
     * hard block for the rest of the selection. Hiding has no eligibility
     * rule (an already-hidden entry is a silent no-op, still counted as
     * "done").
     *
     * @param int[] $studentids
     * @return array{donecount:int,donenames:string[],ignoredcount:int,ignorednames:string[],changed:array<int,bool>}
     *         changed is studentid => new published state, for the caller
     *         to report back to the client and patch just those rows.
     */
    public static function apply(string $screen, int $itemid, array $studentids, string $action, int $userid): array {
        $config = self::SCREENS[$screen];
        $entries = self::get_entries($screen, $itemid);

        $donenames = [];
        $ignorednames = [];
        $changed = [];

        foreach ($studentids as $studentid) {
            $entry = $entries[$studentid] ?? null;
            if (!$entry) {
                // Not part of this item at all — a crafted/stale id, not
                // even worth reporting as "ignored" (that label is reserved
                // for a real dossier that failed an eligibility rule).
                continue;
            }
            $fullname = trim($entry->lastname . ' ' . $entry->firstname);

            $ok = $action === 'publish'
                ? workflow_service::publish($config['domain'], $config['table'], (int) $entry->id, $userid)
                : workflow_service::unpublish($config['domain'], $config['table'], (int) $entry->id, $userid);

            if ($ok) {
                $donenames[] = $fullname;
                $changed[$studentid] = $action === 'publish';
            } else {
                $ignorednames[] = $fullname;
            }
        }

        return [
            'donecount' => count($donenames),
            'donenames' => $donenames,
            'ignoredcount' => count($ignorednames),
            'ignorednames' => $ignorednames,
            'changed' => $changed,
        ];
    }
}
