<?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.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Simulatore contabilità
 *
 * @package    mod_mtcontabilita
 * @copyright  2026 Mediatouch 2000 Srl <https://www.mediatouch.it>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @author     Marco Cappuccio <m.cappuccio@mediatouch.it>
 */

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

/**
 * mtc_get_db
 */
function mtc_get_db($value, int $ndec) {
    if ($value === '' || $value === null) {
        return '';
    }

    $lang = current_language();
    $isitalian = (substr($lang, 0, 2) === 'it');

    $num = (float)$value;

    if ($isitalian) {
        return number_format($num, $ndec, ',', '.');
    } else {
        return number_format($num, $ndec, '.', ',');
    }
}

/**
 * mtc_has_fraction
 */
function mtc_has_fraction($value): bool {
    if ($value === '' || $value === null) {
        return false;
    }
    $n = (float)$value;
    $frac = abs($n - floor($n));
    return $frac > 0.0000001;
}

/**
 * Decide quanti decimali mostrare (0 o 2) in base a bdecimali e ai dati presenti.
 * Non arrotonda: restituisce 0 solo se è certo che NON esistono frazioni > 0 nei record selezionati.
 *
 * @param int $mtcontabilitaid
 * @param int $bdecimali 1|2
 * @return int 0 oppure 2
 */
function mtc_get_ndec_for_mtcontabilita(int $mtcontabilitaid, int $bdecimali): int {
    global $DB;

    // bdecimali=1 => sempre 2
    if ((int)$bdecimali === 1) {
        return 2;
    }

    // bdecimali=2 => 2 solo se esiste una frazione > 0
    $params = [];
    $params['mtid'] = $mtcontabilitaid;

    // Controllo "esiste frazione": amount != FLOOR(amount).
    // Nota: funziona bene se amount è NUMERIC/DECIMAL. Se fosse VARCHAR, va rivista.
    $sql = "SELECT 1
              FROM {mtcontabilita_itemdet}
             WHERE mtcontabilitaid = :mtid
               AND amount IS NOT NULL
               AND amount <> ''
               AND amount <> FLOOR(amount)";

    $hasfraction = $DB->record_exists_sql($sql, $params);

    return $hasfraction ? 2 : 0;
}

/**
 * mtc_set_db
 */
function mtc_set_db($number) {
    $normalizednumber = str_replace(['.', ','], ['', '.'], $number);
    return floatval($normalizednumber);
}

/**
 * Spostamento scritture.
 */
function mtitemnum_move($id, $sort, $move) {
    global $DB;

    $fieldcount = $DB->get_field('mtcontabilita_itemdet', 'MAX(sort)', ['mtcontabilitaid' => $id]);
    if ( ($move == 'up') && ($sort > 1)) {
        $neworder = $sort - 1;
    } else if (($move == 'down') && ($sort < $fieldcount)) {
        $neworder = $sort + 1;
    } else {
        return false;
    }
    $news = $DB->get_records('mtcontabilita_itemdet', ['mtcontabilitaid' => $id, 'sort' => $neworder]);
    $olds = $DB->get_records('mtcontabilita_itemdet', ['mtcontabilitaid' => $id, 'sort' => $sort]);
    foreach ($news as $new) {
        $new->sort = $sort;
        $DB->update_record('mtcontabilita_itemdet', $new);
    }
    foreach ($olds as $old) {
        $old->sort = $neworder;
        $DB->update_record('mtcontabilita_itemdet', $old);
    }
    return true;
}

/**
 * mtitemnum_delete
 */
function mtitemnum_delete($id, $sort) {
    global $DB;

    if (!$DB->delete_records('mtcontabilita_itemdet', array('mtcontabilitaid' => $id, 'sort' => $sort))) {
        throw new \moodle_exception('cannotdeletecustomfield');
    }
    mtitemnum_reorder($id, $sort);
}

/**
 * mtitemnum_reorder
 */
function mtitemnum_reorder($id, $sort) {
    global $DB;

    $i = 0;
    $sql = "SELECT sort FROM {mtcontabilita_itemdet} WHERE mtcontabilitaid=? AND sort>0 GROUP BY sort";
    if ($fields = $DB->get_records_sql($sql, [$id])) {
        foreach ($fields as $field) {
            $DB->set_field('mtcontabilita_itemdet', 'sort', ++$i, ['mtcontabilitaid' => $id, 'sort' => $field->sort]);
        }
    }
}

/**
 * mtcontabilita_question_sql class
 */
class mtcontabilita_question_sql {

    /** @var var */
    private $mtcontabilitaid;

    /**
     * construct
     */
    public function __construct($mtcontabilitaid) {
        $this->mtcontabilitaid = $mtcontabilitaid;
    }

    /**
     * Lista delle scritture.
     */
    public function get_item_dets() {
        global $DB;

        $sql = "SELECT sort, itemnum, itemdate, SUM(score) AS score
                FROM {mtcontabilita_itemdet}
                WHERE sort>:sort AND mtcontabilitaid=:mtcontabilitaid
                GROUP BY sort,itemnum,itemdate
                ORDER BY sort";
        return $DB->get_records_sql($sql, ['sort' => 0, 'mtcontabilitaid' => $this->mtcontabilitaid]);
    }

    /**
     * Lista dettaglio della scrittura.
     */
    public function get_item_dets_list($sort) {
        global $DB;

        $sql = "SELECT i.id,i.itemdate,i.itemtype,i.amount,l.name,l.scheme
        FROM {mtcontabilita_itemdet} i, {mtcontabilita_list} l
        WHERE i.listid=l.id
        AND i.mtcontabilitaid=? AND i.sort=?
        ORDER BY i.sortorder";
        return $DB->get_records_sql($sql, [$this->mtcontabilitaid, $sort]);
    }

}

/**
 * mtcontabilita_question class
 */
class mtcontabilita_question {

    /**
     * Funzioni usate anche dal player.
     */
    public static function display_table_finish($bdecimali, $sez, $alist, $atot, $cmid, $id, $bplayer = false) {
        global $CFG, $OUTPUT;

        $ndec = mtc_get_ndec_for_mtcontabilita($id, $bdecimali);
        // Cerco la colonna con più elementi.
        $max = 0;
        $rows = "";
        foreach ($alist as $key => $val) {
            // Aggiunta riga vuota prima di SP
            if ($key === 'spa') {
                $rows .= '<tr><td colspan="2" style="height: 10px;"></td></tr>';
            }

            $ispn = ($key === 'pna' || $key === 'pnd');
            $style = $ispn ? ' style="border: 2px solid black; background-color: #f9f9f9;"' : '';

            $max = count($alist[$key]) > $max ? count($alist[$key]) : $max;
        }
        // $totsppn = (count($alist['sp2'])+count($alist['pn2']));
        $totsppn = (count($alist['spa']) + count($alist['pna']));
        $max = $totsppn > $max ? $totsppn : $max;
        $str = '<th class="col-1">'.get_string('denominazioneconto', 'mtcontabilita').'</th><td align="right"><b>'.get_string('saldo', 'mtcontabilita').'</b></td>';
        // Riga totali.
        $totcosti = $atot['ced']; // Costi.
        $totricavi = $atot['cea']; // Ricavi.
        // Sempre ricavi - costi.
        $totb = $totricavi - $totcosti;
        $tot1 = $atot['spd'] + $atot['pnd'];
        $tot2 = $atot['spa'] + $atot['pna'];
        if ($bplayer) {
            $msg = '';
            $cls = '';
        } else {
            if ($tot1 == ($tot2 + $totb)) {
                $msg = $OUTPUT->notification(get_string('sisuccess', 'mtcontabilita'), 'success');
                $msg .= '<a href="'.$CFG->wwwroot.'/mod/mtcontabilita/question2/details.php?id='.$cmid.' class="btn btn-primary">'.
                    get_string('insesercizi', 'mtcontabilita').'</a>';
                $cls = '';
            } else {
                // "Verificare il totale ATTIVO PASSIVO".
                $msg = $OUTPUT->notification(get_string('sierror', 'mtcontabilita'), 'error');
                $cls = ' style="color:#FF0000" ';
            }
        }
        // *********************************************************************
        $strtotb = '<tr>';
        $strtotb .= '<th>&nbsp;</th><th>&nbsp;</th>';
        $strtotb .= '<th>&nbsp;</th><th>&nbsp;</th>';
        $cspan = 4;
        switch ($sez) {
            case "sp":
                $thead = '<th class="col-4" colspan="4"><div align="center">'.get_string('statopatrimoniale', 'mtcontabilita').'</div></th>';
                $thead2 = '<th class="col-2" colspan="2"><div align="center">'.get_string('attivo', 'mtcontabilita').'</div></th>
                <th class="col-2" colspan="2"><div align="center">'.get_string('passivor', 'mtcontabilita').'</div></th>';
                $thead3 = $str.$str;
                $strtot = '<td>Tot '.get_string('attivo', 'mtcontabilita').'</td><td'.$cls.' align="right"><b>'.mtc_get_db(abs($tot1), $ndec).'</b></td>';
                $strtot .= '<td>Tot '.get_string('passivor', 'mtcontabilita').'</td><td'.$cls.' align="right"><b>'.mtc_get_db(abs($tot2 + $totb), $ndec).'</b></td>';
                $elementi = ['spd', 'sppn2'];
                break;
            case "ce":
                $thead = '<th class="col-4" colspan="4"><div align="center">'.get_string('contoeconomico', 'mtcontabilita').'</div></th>';
                $thead2 = '<th class="col-2" colspan="2"><div align="center">'.get_string('costi', 'mtcontabilita').'</div></th>
                <th class="col-2" colspan="2"><div align="center">'.get_string('ricavi', 'mtcontabilita').'</div></th>';
                $thead3 = $str.$str;
                // Stabilisco a chi devo sommare utile/perdita.
                if ($totricavi >= $totcosti) {
                    $aa = $totcosti + abs($totb);
                    $bb = $totricavi;
                } else {
                    $aa = $totcosti;
                    $bb = $totricavi + abs($totb);
                }
                $strtot = '<td>Tot '.get_string('costi', 'mtcontabilita').'</td><td align="right"><b>'.mtc_get_db(abs($aa), $ndec).'</b></td>';
                $strtot .= '<td>Tot '.get_string('ricavi', 'mtcontabilita').'</td><td align="right"><b>'.mtc_get_db(abs($bb), $ndec).'</b></td>';
                $strtotb = "";
                $elementi = ['ced', 'cea'];
                break;
        }
        $strtotb .= '</tr>';
        // *********************************************************************
        // $alist['sppn2'] = array_merge($alist['pn2'],$alist['sp2']);
        // $totpn = count($alist['pn2']);
        $alist['sppn2'] = array_merge($alist['pna'], $alist['spa']);
        $totpn = count($alist['pna']);
        // ===============================
        // Ciclo principale con box PN e utile/perdita incluso al momento giusto
        // ===============================
        $body = "";
        $totpn = count($alist['pna']);

        for ($r = 0; $r < $max; $r++) {
            $body .= '<tr>';

            foreach ($elementi as $nonserve => $i) {
                $clsx = $cldx = "";
                if (isset($alist[$i][$r])) {
                    $body .= '<td'.$clsx.'>'.$alist[$i][$r][0].'</td><td align="right"'.$cldx .'>'.mtc_get_db(abs($alist[$i][$r][1]), $ndec).'</td>';
                } else {
                    $body .= '<td>&nbsp;</td><td>&nbsp;</td>';
                }
            }
            $body .= '</tr>';
        }
        // Riga vuota tra PN e SP.
        if ($r + 1 == $totpn) {
            $body .= '<tr><td colspan="4" style="height: 10px;"></td></tr>';
        }
        // *********************************************************************
        $table = '';
        $table .= '<table class="table table-bordered">';
        $table .= '<thead>';
        $table .= '<tr>'.$thead.'</tr>';
        $table .= '<tr>'.$thead2.'</tr>';
        $table .= '<tr>'.$thead3.'</tr>';
        $table .= '</thead>';
        $table .= '<tbody>';
        $table .= $body;
        $table .= $strtotb;
        $table .= '<tr><td colspan="'.$cspan.'">&nbsp;</td></tr>';
        if ($sez == 'ce') {
            if ($totricavi >= $totcosti) {
                $table .= '<tr><td>'.get_string('utileperdita1', 'mtcontabilita').'</td><td align="right"><b><span style="color:green">'.
                    mtc_get_db(abs($totb), $ndec).'</span></b></td></tr>';
            } else {
                $table .= '<tr><td>&nbsp;</td><td>&nbsp;</td><td>'.get_string('utileperdita2', 'mtcontabilita').'</td><td align="right"><b><span style="color:red">'.
                    mtc_get_db(abs($totb), $ndec).'</span></b></td></tr>';
            }
        } else {
            $label = $totb < 0 ? 'utileperdita2' : 'utileperdita1';
            if ($totb < 0) {
                $strtotb = '<b><span style="color:red">('.mtc_get_db(abs($totb), $ndec).')</span></b>';
            } else {
                $strtotb = '<b><span style="color:green">'.mtc_get_db(abs($totb), $ndec).'</span></b>';
            }
            $table .= '<tr><td>&nbsp;</td><td>&nbsp;</td><td>'.get_string($label, 'mtcontabilita').'</td><td align="right">'.$strtotb.'</td></tr>';
        }
        $table .= '<tr>'.$strtot.'</tr>';
        $table .= '</tbody>';
        $table .= '</table>';
        return [$table, $msg];
    }

    /**
     * get_tipologia
     */
    public static function get_tipologia($val) {
        $array = ["dare", "avere"];
        return $array[$val];
    }

    /**
     * get_users_listing
     */
    public static function get_users_listing($sort='descrizione', $dir='ASC', $page=0, $recordsperpage=0,
                               $search='', $firstinitial='', $lastinitial='', $extraselect='',
                               array $extraparams=null, $extracontext = null) {
        global $DB, $CFG;

        $select = "id>0";
        $params = [];

        if (!empty($search)) {
            $search = trim($search);
            $select .= " AND (". $DB->sql_like($fullname, ':search1', false, false).
                       " OR ". $DB->sql_like('email', ':search2', false, false).
                       " OR username = :search3)";
            $params['search1'] = "%$search%";
            $params['search2'] = "%$search%";
            $params['search3'] = "$search";
        }

        if ($firstinitial) {
            $select .= " AND ". $DB->sql_like('firstname', ':fni', false, false);
            $params['fni'] = "$firstinitial%";
        }
        if ($lastinitial) {
            $select .= " AND ". $DB->sql_like('lastname', ':lni', false, false);
            $params['lni'] = "$lastinitial%";
        }

        if ($sort) {
            $orderbymap = [];
            $orderbymap['default'] = 'descrizione';
            $sort = get_safe_orderby($orderbymap, $sort, $dir);
        }

        $sql = "SELECT * FROM {mtcontabilita_piani} WHERE $select $sort";
        return $DB->get_records_sql($sql, $params, $page, $recordsperpage);

    }

    /**
     * get users
     */
    public static function get_users($get=true, $search='', $confirmed=false, array $exceptions=null, $sort='firstname ASC',
                       $firstinitial='', $lastinitial='', $page='', $recordsperpage='', $fields='*', $extraselect='', array $extraparams=null) {
        global $DB, $CFG;

        if ($get && !$recordsperpage) {
            debugging('Call to get_users with $get = true no $recordsperpage limit. ' .
                    'On large installations, this will probably cause an out of memory error. ' .
                    'Please think again and change your code so that it does not try to ' .
                    'load so much data into memory.', DEBUG_DEVELOPER);
        }

        $fullname  = $DB->sql_fullname();

        $select = "";
        $params = [];

        if (!empty($search)) {
            $search = trim($search);
            $select .= " AND (".$DB->sql_like($fullname, ':search1', false)." OR ".$DB->sql_like('email', ':search2', false)." OR username = :search3)";
            $params['search1'] = "%$search%";
            $params['search2'] = "%$search%";
            $params['search3'] = "$search";
        }

        if ($confirmed) {
            $select .= " AND confirmed = 1";
        }

        if ($exceptions) {
            list($exceptions, $eparams) = $DB->get_in_or_equal($exceptions, SQL_PARAMS_NAMED, 'ex', false);
            $params = $params + $eparams;
            $select .= " AND id $exceptions";
        }

        if ($firstinitial) {
            $select .= " AND ".$DB->sql_like('firstname', ':fni', false, false);
            $params['fni'] = "$firstinitial%";
        }
        if ($lastinitial) {
            $select .= " AND ".$DB->sql_like('lastname', ':lni', false, false);
            $params['lni'] = "$lastinitial%";
        }

        if ($extraselect) {
            $select .= " AND $extraselect";
            $params = $params + (array)$extraparams;
        }

        if ($get) {
            return $DB->get_records_select('user', $select, $params, $sort, $fields, $page, $recordsperpage);
        } else {
            return $DB->count_records_select('user', $select, $params);
        }
    }
}
