<?php

namespace App\Models;

use CodeIgniter\Model;
use App\Libraries\Gsb_lib;

/**
 * Modèle principal de l'application GSBFrais.
 * Contient toutes les requêtes vers la base de données
 * (utilisateurs, fiches de frais, frais forfait et hors forfait).
 */
class GsbModel extends Model
{
    /**
     * Retourne les informations d'un utilisateur à partir de son login et mot de passe.
     *
     * @param string $login Login de l'utilisateur
     * @param string $mdp   Mot de passe de l'utilisateur
     * @return array|null   Infos de l'utilisateur ou null si non trouvé
     */
    public function get_infos_utilisateur($login, $mdp)
    {
        return $this->db->table('infos_utilisateur')
            ->select('idutilisateur, nom, prenom, login, mdp, libelle, idRole')
            ->where('login', $login)
            ->where('mdp', $mdp)
            ->get()
            ->getRowArray();
    }

    /**
     * Retourne les détails d'un utilisateur.
     *
     * @param string $id Identifiant de l'utilisateur
     * @return array|null
     */
    public function get_detail_utilisateur($id)
    {
        return $this->db->table('utilisateur')
            ->where('idutilisateur', $id)
            ->get()
            ->getRowArray();
    }

    /**
     * Retourne les mois pour lesquels le visiteur a des fiches de frais.
     *
     * @param string $idVisiteur Identifiant du visiteur
     * @return array Liste des couples année/mois
     */
    public function get_les_mois_disponibles($idVisiteur)
    {
        return $this->db->table('fichefrais')
            ->select('CONCAT(annee,mois) AS "anneemois", annee, mois')
            ->where('idVisiteur', $idVisiteur)
            ->orderBy('annee', 'DESC')
            ->orderBy('mois', 'DESC')
            ->get()
            ->getResultArray();
    }

    /**
     * Retourne l'id et l'état de la fiche de frais d'un visiteur pour un mois donné.
     *
     * @param string $idVisiteur Identifiant du visiteur
     * @param int    $annee      Année
     * @param int    $mois       Mois
     * @return array|null
     */
    public function get_id_ficheFrais($idVisiteur, $annee, $mois)
    {
        return $this->db->table('fichefrais')
            ->select('fichefrais.idFiche, fichefrais.idEtat')
            ->where('fichefrais.idVisiteur', $idVisiteur)
            ->where('fichefrais.annee', $annee)
            ->where('fichefrais.mois', $mois)
            ->get()
            ->getRowArray();
    }

    /**
     * Retourne les infos détaillées d'une fiche de frais (avec son libellé d'état).
     *
     * @param int $idFiche Identifiant de la fiche
     * @return array|null
     */
    public function get_les_infos_ficheFrais($idFiche)
    {
        return $this->db->table('fichefrais')
            ->select('fichefrais.idFiche, fichefrais.idEtat, fichefrais.dateModif, fichefrais.nbJustificatifs, fichefrais.montantValide, etatfrais.libelle')
            ->join('etatfrais', 'fichefrais.idEtat = etatfrais.idetat')
            ->where('fichefrais.idFiche', $idFiche)
            ->get()
            ->getRowArray();
    }

    /**
     * Retourne les frais forfait (libellé, quantité) d'une fiche.
     *
     * @param int $idFiche Identifiant de la fiche
     * @return array
     */
    public function get_les_frais_forfait($idFiche)
    {
        return $this->db->table('lignefraisforfait')
            ->select('fraisforfait.idfraisforfait, fraisforfait.libelle, lignefraisforfait.quantite')
            ->join('fraisforfait', 'fraisforfait.idfraisforfait = lignefraisforfait.idFraisForfait')
            ->where('lignefraisforfait.idFiche', $idFiche)
            ->orderBy('lignefraisforfait.idFraisForfait', 'ASC')
            ->get()
            ->getResultArray();
    }

    /**
     * Retourne les frais hors forfait d'une fiche.
     *
     * @param int $idFiche Identifiant de la fiche
     * @return array
     */
    public function get_les_frais_hors_forfait($idFiche)
    {
        return $this->db->table('lignefraishorsforfait')
            ->where('idFiche', $idFiche)
            ->get()
            ->getResultArray();
    }

    /**
     * Vérifie si c'est le premier frais saisi pour un mois donné.
     *
     * @param string $idVisiteur
     * @param int    $annee
     * @param int    $mois
     * @return bool True si aucune fiche n'existe pour ce mois
     */
    public function est_premier_frais_mois($idVisiteur, $annee, $mois)
    {
        $row = $this->db->table('fichefrais')
            ->select('count(*) AS nblignesfrais')
            ->where('idVisiteur', $idVisiteur)
            ->where('annee', $annee)
            ->where('mois', $mois)
            ->get()
            ->getRowArray();
        return $row['nblignesfrais'] === "0";
    }

    /**
     * Retourne le dernier mois saisi (format anneemois) pour un visiteur.
     *
     * @param string $idVisiteur
     * @return string
     */
    public function dernier_mois_saisi($idVisiteur)
    {
        $row = $this->db->table('fichefrais')
            ->select('max(CONCAT(annee,mois)) AS dernierAnneeMois')
            ->where('idVisiteur', $idVisiteur)
            ->get()
            ->getRowArray();
        return $row['dernierAnneeMois'];
    }

    /**
     * Retourne tous les identifiants des frais forfait.
     *
     * @return array
     */
    public function get_les_id_frais_forfait()
    {
        return $this->db->table('fraisforfait')
            ->select('idfraisforfait')
            ->orderBy('idfraisforfait')
            ->get()
            ->getResultArray();
    }

    /**
     * Crée une nouvelle fiche de frais et ses lignes forfait à 0.
     * Clôture la dernière fiche en état CR si elle existe.
     *
     * @param string $idVisiteur
     * @param int    $annee
     * @param int    $mois
     * @return bool True si tout s'est bien passé
     */
    public function cree_nouvelles_lignes_frais($idVisiteur, $annee, $mois)
    {
        $dernierMois = $this->dernier_mois_saisi($idVisiteur);
        $gsb_lib = new Gsb_lib();
        $num_annee = $gsb_lib->get_annee_from_anneemois($dernierMois);
        $num_mois = $gsb_lib->get_mois_from_anneemois($dernierMois);

        $laDerniereFiche = $this->get_id_ficheFrais($idVisiteur, $num_annee, $num_mois);

        // Clôture la fiche précédente si elle est en cours
        if ($laDerniereFiche != null && $laDerniereFiche['idEtat'] == 'CR') {
            $idDerniereFiche = $laDerniereFiche['idFiche'];
            $this->maj_etat_fiche_frais($idDerniereFiche, 'CL');
        }

        $resultat = $this->db->table('fichefrais')->insert([
            'idVisiteur' => $idVisiteur,
            'annee' => $annee,
            'mois' => $mois,
            'nbJustificatifs' => 0,
            'montantValide' => 0,
            'dateModif' => date('Y-m-d'),
            'idEtat' => 'CR'
        ]);

        $insertionsOK = true;
        if ($resultat) {
            // Récupère l'id de la fiche créée pour y ajouter les lignes forfait
            $nouvelleFiche = $this->get_id_ficheFrais($idVisiteur, $annee, $mois);
            $idNouvelleFiche = $nouvelleFiche['idFiche'];
            foreach ($this->get_les_id_frais_forfait() as $unIdFraisForfait) {
                $res = $this->db->table('lignefraisforfait')->insert([
                    'idFiche' => $idNouvelleFiche,
                    'idFraisForfait' => $unIdFraisForfait['idfraisforfait'],
                    'quantite' => 0
                ]);
                if (!$res) {
                    $insertionsOK = false;
                    break;
                }
            }
        } else {
            return false;
        }
        return $insertionsOK;
    }

    /**
     * Met à jour l'état d'une fiche de frais.
     *
     * @param int    $idFiche
     * @param string $etat    Code de l'état (CR, CL, VA, RB)
     */
    public function maj_etat_fiche_frais($idFiche, $etat)
    {
        $this->db->table('fichefrais')->update(
            ['idEtat' => $etat, 'dateModif' => date('Y-m-d')],
            ['idFiche' => $idFiche]
        );
    }

    /**
     * Met à jour les quantités des frais forfait d'une fiche.
     *
     * @param int   $idFiche
     * @param array $lesFrais Tableau associatif [idFraisForfait => quantite]
     * @return bool True si toutes les mises à jour ont réussi
     */
    public function maj_frais_forfait($idFiche, array $lesFrais)
    {
        $majOK = true;
        foreach (array_keys($lesFrais) as $idFrais) {
            $res = $this->db->table('lignefraisforfait')->update(
                ['quantite' => $lesFrais[$idFrais]],
                ['idFiche' => $idFiche, 'idFraisForfait' => $idFrais]
            );
            if (!$res) {
                $majOK = false;
                break;
            }
        }
        return $majOK;
    }

    /**
     * Supprime un frais hors forfait via la procédure stockée SupprimerFraisHorsForfait.
     *
     * @param int $idFrais
     * @return mixed
     */
    public function supprimer_frais_hors_forfait($idFrais)
    {
        return $this->db->query("CALL SupprimerFraisHorsForfait($idFrais)");
    }

    /**
     * Crée un nouveau frais hors forfait.
     *
     * @param int    $idFiche
     * @param string $libelle
     * @param string $date    Date au format Y-m-d
     * @param float  $montant
     * @return bool
     */
    public function creer_nouveau_frais_hors_forfait($idFiche, $libelle, $date, $montant)
    {
        $resultat = $this->db->table('lignefraishorsforfait')->insert([
            'idFiche' => $idFiche,
            'libelle' => $libelle,
            'dateFrais' => $date,
            'montant' => $montant
        ]);
        return $resultat;
    }

    /**
     * Retourne les visiteurs ayant des fiches dans un état donné.
     *
     * @param string $idEtat Code de l'état (CR, CL, VA, RB)
     * @return array
     */
    public function get_visiteurs_fiches_etat($idEtat)
    {
        return $this->db->table('fichefrais')
            ->select('utilisateur.idutilisateur, utilisateur.nom, utilisateur.prenom, fichefrais.idFiche, fichefrais.annee, fichefrais.mois, fichefrais.idEtat')
            ->join('utilisateur', 'fichefrais.idVisiteur = utilisateur.idutilisateur')
            ->where('utilisateur.idRole', 'VM')
            ->where('fichefrais.idEtat', $idEtat)
            ->orderBy('utilisateur.nom', 'ASC')
            ->get()
            ->getResultArray();
    }

    /**
     * Retourne les infos du mot de passe d'un utilisateur (mdp, date de création).
     *
     * @param string $idutilisateur
     * @return array|null
     */
    public function get_infos_mdp($idutilisateur)
    {
        return $this->db->table('utilisateur')
            ->select('idutilisateur, mdp, dateCreationMdp')
            ->where('idutilisateur', $idutilisateur)
            ->get()
            ->getRowArray();
    }

    /**
     * Met à jour le mot de passe d'un utilisateur.
     *
     * @param string $idutilisateur
     * @param string $nouveauMdp
     * @return bool
     */
    public function maj_motdepasse($idutilisateur, $nouveauMdp)
    {
        return $this->db->table('utilisateur')
            ->where('idutilisateur', $idutilisateur)
            ->update([
                'mdp' => $nouveauMdp,
                'dateCreationMdp' => date('Y-m-d')
            ]);
    }

    /**
     * Passe une fiche de l'état "Validée" (VA) à "Remboursée" (RB).
     *
     * @param string $idvisiteur
     * @param int    $annee
     * @param int    $mois
     * @return bool
     */
    public function maj_etat_fiches_rembourse($idvisiteur, $annee, $mois)
    {
        return $this->db->table('fichefrais')
            ->where('idEtat', 'VA')
            ->where('idVisiteur', $idvisiteur)
            ->where('annee', $annee)
            ->where('mois', $mois)
            ->update(['idEtat' => 'RB']);
    }

    /**
     * Calcule le montant total d'une fiche (frais forfait + frais hors forfait non refusés).
     *
     * @param int $idFiche
     * @return float
     */
    public function get_montant_fraisforfait_fraishorsforfait($idFiche)
    {
        $total = 0;

        $lesFraisForfait = $this->db->table('lignefraisforfait')
            ->join('fraisforfait', 'lignefraisforfait.idFraisForfait = fraisforfait.idfraisforfait')
            ->where('idFiche', $idFiche)
            ->get()
            ->getResultArray();

        foreach ($lesFraisForfait as $frais) {
            $total += ($frais['quantite'] * $frais['montant']);
        }

        $lesFraisHorsForfait = $this->db->table('lignefraishorsforfait')
            ->where('idFiche', $idFiche)
            ->notLike('libelle', 'REFUSE')
            ->get()
            ->getResultArray();

        foreach ($lesFraisHorsForfait as $fraisHF) {
            $total += $fraisHF['montant'];
        }

        return $total;
    }

    /**
     * Reporte un frais hors forfait sur la fiche du mois suivant.
     * Crée la fiche du mois suivant si elle n'existe pas.
     *
     * @param int $idFrais Identifiant du frais à reporter
     * @return bool
     */
    public function reporter_frais_hors_forfait_mois_suivant($idFrais)
    {
        $frais = $this->db->table('lignefraishorsforfait')
            ->where('idLigneFHF', $idFrais)
            ->get()
            ->getRowArray();

        if ($frais == null) {
            return false;
        }

        $ficheActuelle = $this->db->table('fichefrais')
            ->where('idFiche', $frais['idFiche'])
            ->get()
            ->getRowArray();

        if ($ficheActuelle == null) {
            return false;
        }

        $annee = (int) $ficheActuelle['annee'];
        $mois = (int) $ficheActuelle['mois'];

        // Calcul du mois suivant
        if ($mois == 12) {
            $moisSuivant = 1;
            $anneeSuivante = $annee + 1;
        } else {
            $moisSuivant = $mois + 1;
            $anneeSuivante = $annee;
        }

        $idVisiteur = $ficheActuelle['idVisiteur'];

        $ficheSuivante = $this->get_id_ficheFrais($idVisiteur, $anneeSuivante, $moisSuivant);

        // Crée la fiche du mois suivant si elle n'existe pas
        if ($ficheSuivante == null) {
            $this->db->table('fichefrais')->insert([
                'idVisiteur'      => $idVisiteur,
                'annee'           => $anneeSuivante,
                'mois'            => $moisSuivant,
                'nbJustificatifs' => 0,
                'montantValide'   => 0,
                'dateModif'       => date('Y-m-d'),
                'idEtat'          => 'CR'
            ]);

            $ficheSuivante = $this->get_id_ficheFrais($idVisiteur, $anneeSuivante, $moisSuivant);

            foreach ($this->get_les_id_frais_forfait() as $unIdFraisForfait) {
                $this->db->table('lignefraisforfait')->insert([
                    'idFiche'        => $ficheSuivante['idFiche'],
                    'idFraisForfait' => $unIdFraisForfait['idfraisforfait'],
                    'quantite'       => 0
                ]);
            }
        }

        // Insère le frais dans la fiche suivante
        $this->db->table('lignefraishorsforfait')->insert([
            'idFiche'   => $ficheSuivante['idFiche'],
            'libelle'   => $frais['libelle'],
            'dateFrais' => $frais['dateFrais'],
            'montant'   => $frais['montant']
        ]);

        // Supprime le frais de la fiche actuelle
        $this->db->table('lignefraishorsforfait')
            ->where('idLigneFHF', $idFrais)
            ->delete();

        return true;
    }

    /**
     * Refuse un frais hors forfait en préfixant son libellé par "REFUSE :".
     *
     * @param int $idFrais
     */
    public function refuser_frais_hors_forfait($idFrais)
    {
        $fraishorsforfait = $this->db->table('lignefraishorsforfait')
            ->where('idLigneFHF', $idFrais)
            ->notLike('libelle', 'REFUSE')
            ->get()
            ->getRowArray();

        if ($fraishorsforfait != null) {
            $this->db->table('lignefraishorsforfait')
                ->where('idLigneFHF', $idFrais)
                ->update(['libelle' => 'REFUSE : ' . $fraishorsforfait['libelle']]);
        }
    }

    /**
     * Passe une fiche de l'état "Clôturée" (CL) à "Validée" (VA) et sauvegarde le montant.
     *
     * @param string $idvisiteur
     * @param int    $annee
     * @param int    $mois
     * @return bool
     */
    public function maj_etat_fiches_valider($idvisiteur, $annee, $mois)
    {
        $fiche = $this->get_id_ficheFrais($idvisiteur, $annee, $mois);
        $montant = $this->get_montant_fraisforfait_fraishorsforfait($fiche['idFiche']);

        return $this->db->table('fichefrais')
            ->where('idEtat', 'CL')
            ->where('idVisiteur', $idvisiteur)
            ->where('annee', $annee)
            ->where('mois', $mois)
            ->update([
                'idEtat' => 'VA',
                'montantValide' => $montant
            ]);
    }
}
