<?php

namespace App\Imports;

use App\Models\Categorie;
use App\Models\Direction;
use App\Models\Domaine;
use App\Models\Famille;
use App\Models\ImputationBudgetaire;
use App\Models\NiveauMutualisation;
use App\Models\OperateurMutualisation;
use App\Models\Paat;
use App\Models\Projet;
use App\Models\ProjetStatut;
use App\Models\Segment;
use App\Models\Service;
use App\Models\Statut;
use App\Models\Structure;
use App\Models\User;
use App\Models\GainAchatComplexeProjet;
use App\Notifications\Import\ImportNotification;
use Carbon\Carbon;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Events\AfterImport;
use Maatwebsite\Excel\Events\ImportFailed;
use PAAT_STATUT;
use PROJET_STATUT;
use TYPE_CALCUL_GAINS;
use PROJET as PROJET_CONSTANTS;

class ProjetsImport implements ToCollection, WithHeadingRow
{
    public $importedBy;
    public $contexte = 'projet';
    public $insertedProjets = array();
    public $skippedProjets = 0;
    public $incoherentProjets = 0;
    public $headersRequired = ['nom_support_bdd', 'nom_de_letab_concerne_par_le_projet', 'budget_principal', 'date_de_debut_notification_du_marche_lancement_de_laction_ddmmaaaa', 'mutualisation', 'operateur_de_mutualisation', 'code_comptable', 'code_etab_finess', 'numero_projet', 'famille', 'domaine', 'categorie', 'segment', 'intitule_du_projet_achats', 'description', 'montant_de_reference_annuel_eur_ttc', 'gains_identifies_base_12_mois_eur_ttc', 'gains_notifies_base_12_mois_eur_ttc', 'gains_achats_au_sens_ondam_du_terme', 'hors_programmation', 'priorite', 'statut'];
    public $beforeImportSuccess = true;

    public function __construct(User $importedBy)
    {
        $this->importedBy = $importedBy;
    }

    /**
     * @param Collection $rows
     */
    public function collection(Collection $rows)
    {
        // On importe pas les element si le header ne correspond pas
        if ($this->beforeImportSuccess) {

            foreach ($rows as $row) {
                if (isset($row['nom_support_bdd']) && $row['nom_support_bdd'] != null) {
                    $structure_id = Structure::where('nom', trim($row['nom_support_bdd']))->where('active', true)->first()->id ?? null;
                    // On vérifie que l'on a bien une structure et que l'utilisateur en fait partie
                    if ($structure_id && $structure_id === $this->importedBy->structure_id) {
                        // Si on a la structure, on récupère tous les éléments en bdd
                        $direction_id = Direction::where('nom', trim($row['nom_de_letab_concerne_par_le_projet']))
                                ->where('structure_id', $structure_id)->first()->id ?? null;

                        if ($direction_id) {
                            // On récupère / crée un service avec la colonne budget principal
                            $service = Service::firstOrCreate([
                                'nom' => trim($row['budget_principal']),
                                'direction_id' => $direction_id
                            ]);

                            // On récupère / crée le paat en fonction de la date de notification
                            $date_notification = Carbon::parse(gmdate('m/d/y', ($row['date_de_debut_notification_du_marche_lancement_de_laction_ddmmaaaa'] - 25569) * 86400));
                            $paat = Paat::where('annee_reference', $date_notification->year)->where('structure_id', $structure_id)->first();
                            if (!$paat) {
                                $paat = Paat::firstOrCreate([
                                    'statut' => PAAT_STATUT::SOUMIS,
                                    'annee_reference' => $date_notification->year,
                                    'description' => 'PAAT ' . $date_notification->year,
                                    'date_debut' => $date_notification->copy()->startOfYear(),
                                    'date_fin' => $date_notification->copy()->endOfYear(),
                                    'structure_id' => $structure_id
                                ]);
                            }

                            // On récupère / crée le niveau de mutualisation
                            $niveauMutualisation = NiveauMutualisation::firstOrCreate([
                                'nom' => trim($row['mutualisation']),
                                'structure_id' => $structure_id
                            ]);

                            // On récupère / crée l'opérateur de mutualisation
                            $operateurMutualisation = OperateurMutualisation::firstOrCreate([
                                'nom' => trim($row['operateur_de_mutualisation']),
                                'structure_id' => $structure_id
                            ]);

                            // On récupère / crée l'imputation budgetaire
                            $imputationBudgetaire = ImputationBudgetaire::firstOrCreate([
                                'nom' => trim($row['code_comptable']),
                                'structure_id' => $structure_id
                            ]);

                            // On récupère le chef de projet si possible
                            // Sinon on utilise l'utilisateur courant
                            $CdP = explode(" ", trim(""));
                            $owner_id = User::where('structure_id', $structure_id)->where('prenom', $CdP[0] ?? null)->where('nom', $CdP[1] ?? null)->first()->id ??
                                $this->importedBy->getAuthIdentifier();

                            // Si le pourcentage calculé depuis le fichier est < 0 et > 100 on le sauvegarde pour pouvoir avertir l'utilisateur
                            $pourcentage = $row['montant_de_reference_annuel_eur_ttc'] == null || $row['montant_de_reference_annuel_eur_ttc'] == 0 ?
                                null : round($row['gains_identifies_base_12_mois_eur_ttc'] * 100 / $row['montant_de_reference_annuel_eur_ttc'], 2);
                            if ($pourcentage < 0 && $pourcentage > 100) {
                                $this->incoherentProjets++;
                            }

                            $montant_reference = $row['montant_de_reference_annuel_eur_ttc'];
                            $montant_notifie = $row['gains_notifies_base_12_mois_eur_ttc'] == null || $row['montant_de_reference_annuel_eur_ttc'] == null ? null : ($row['montant_de_reference_annuel_eur_ttc'] - $row['gains_notifies_base_12_mois_eur_ttc']) ?? null;
                            $montant_realise = $row['gains_realises_base_12_mois_eur_ttc'] == null || $row['montant_de_reference_annuel_eur_ttc'] == null ? null : ($row['montant_de_reference_annuel_eur_ttc'] - $row['gains_realises_base_12_mois_eur_ttc']) ?? null;

                            // Récupération de la nomenclature en respectant la hierarchie
                            $famille_id = Famille::where('nom', $row['famille'] ?? null)->first()->id ?? null;
                            $domaine_id = null;
                            $categorie_id = null;
                            $segment_id = null;
                            if (isset($famille_id)) {
                                $domaine_id = Domaine::where('nom', $row['domaine'] ?? null)->where('famille_id', $famille_id)->first()->id ?? null;
                                if (isset($domaine_id)) {
                                    $categorie_id = Categorie::where('nom', $row['categorie'] ?? null)->where('domaine_id', $domaine_id)->first()->id ?? null;
                                    if (isset($categorie_id)) {
                                        $segment_id = Segment::where('nom', $row['segment'] ?? null)->where('categorie_id', $categorie_id)->first()->id ?? null;
                                    }
                                }
                            }

                            $projet = Projet::firstOrCreate([
                                'structure_id' => $structure_id,
                                'finess' => $row['code_etab_finess'],
                                'reference' => $row['numero_projet'],
                                'direction_id' => $direction_id,
                                'famille_id' => $famille_id,
                                'domaine_id' => $domaine_id,
                                'categorie_id' => $categorie_id,
                                'segment_id' => $segment_id,
                                'objet' => $row['intitule_du_projet_achats'],
                                'description' => $row['description'],
                                'niveau_mutualisation_id' => $niveauMutualisation->id,
                                'operateur_mutualisation_id' => $operateurMutualisation->id,
                                'date_mise_en_oeuvre' => $date_notification,
                                'montant_reference' => $montant_reference,
                                'pourcentage_gains_cible' => $pourcentage,
                                'montant_notifie' => $montant_notifie,
                                'montant_realise' => $montant_realise,
                                'gains_cible' => $montant_reference != null && $pourcentage != null ? $montant_reference * $pourcentage / 100 : null,
                                'gains_notifie' => $row['gains_notifies_base_12_mois_eur_ttc'],
                                'ondam' => $row['gains_achats_au_sens_ondam_du_terme'] === 'OUI' ? true : false,
                                'programmation' => $row['hors_programmation'] === 'OUI' ? false : true,
                                'niveau_priorite' => $row['priorite'],
                                'duree_mois' => 12,
                                'owner_id' => $owner_id,
                                'paat_id' => $paat->id,
                                'date_debut' => $paat->date_debut,
                                'date_fin' => $date_notification,
                                'type_calcul_gains' => TYPE_CALCUL_GAINS::ACHATS_RECURRENTS,
                                'service_id' => $service->id,
                                'imputation_budgetaire_id' => $imputationBudgetaire->id,
                                'budget' => $row['investissement_exploitation'] === PROJET_CONSTANTS::PROJET_FORMATTED[PROJET_CONSTANTS::INVESTISSEMENT] ? PROJET_CONSTANTS::INVESTISSEMENT : PROJET_CONSTANTS::EXPLOITATION,
                                'renouvellement_marche' => null
                            ]);

                            if ($projet) {
                                $statut = $row['statut'] == 'Terminé' ? 'Clôturée' : $row['statut'];
                                ProjetStatut::firstOrCreate([
                                    'projet_id' => $projet->id,
                                    'user_id' => $owner_id,
                                    'statut_id' => Statut::where('nom', $statut)->first()->id ?? PROJET_STATUT::EN_COURS
                                ]);

                                // On sauvegarde l'id du projet inséré pour pouvoir rollback si nécessaire
                                array_push($this->insertedProjets, $projet->id);

                                // On enregistre la ventilation des gains cibles, au cas où la fiche projet est sujette à un report de PAAT
                                $nbJoursN = floor((strtotime($projet->paat->date_fin) - strtotime($projet->date_fin)) / (3600 * 24));
                                $gains_cibles_N = ceil($projet->gains_cible * $nbJoursN / 364);
                                $gains_cibles_N1 = ceil($projet->gains_cible * (364 - $nbJoursN) / 364);

                                $gains_achats_complexes = [];
                                array_push($gains_achats_complexes, [
                                    "type" => "cible",
                                    "annee" => date('Y', strtotime($projet->date_debut)),
                                    "valeur" => $gains_cibles_N,
                                    "projet_id" => $projet->id
                                ]);
                                array_push($gains_achats_complexes, [
                                    "type" => "cible",
                                    "annee" => (date('Y', strtotime($projet->date_debut)) + 1),
                                    "valeur" => $gains_cibles_N1,
                                    "projet_id" => $projet->id
                                ]);
                                GainAchatComplexeProjet::insert($gains_achats_complexes);

                                if ($projet->date_mise_en_oeuvre != null) {
                                    // On enregistre la ventilation des gains cibles, au cas où la fiche projet est sujette à un report de PAAT
                                    // TODO : petit décalage car $projet->date_mise_en_oeuvre garde les heures à l'enregistrement alors que pas la date de fin du PAAT
                                    $nbJoursAnneeNDateNotification = floor((strtotime($projet->paat->date_fin) -  date_timestamp_get($projet->date_mise_en_oeuvre)) / (3600 * 24));
                                    $gains_notifie_N = ceil(($projet->gains_notifie * $nbJoursAnneeNDateNotification) / 364);
                                    $gains_notifie_N1 = ceil($projet->gains_notifie * ((364 - $nbJoursAnneeNDateNotification) / 364));

                                    $gains_achats_complexes = [];
                                    array_push($gains_achats_complexes, [
                                        "type" => "notifie",
                                        "annee" => date('Y', strtotime($projet->date_debut)),
                                        "valeur" => $gains_notifie_N,
                                        "projet_id" => $projet->id
                                    ]);
                                    array_push($gains_achats_complexes, [
                                        "type" => "notifie",
                                        "annee" => (date('Y', strtotime($projet->date_debut)) + 1),
                                        "valeur" => $gains_notifie_N1,
                                        "projet_id" => $projet->id
                                    ]);
                                    GainAchatComplexeProjet::insert($gains_achats_complexes);

                                    // On enregistre la ventilation des gains cibles, au cas où la fiche projet est sujette à un report de PAAT
                                    $gains_realise_N = ceil(($projet->gains_securise * $nbJoursAnneeNDateNotification) / 364);
                                    $gains_realise_N1 = ceil($projet->gains_securise * ((364 - $nbJoursAnneeNDateNotification) / 364));

                                    $gains_achats_complexes = [];
                                    array_push($gains_achats_complexes, [
                                        "type" => "securise",
                                        "annee" => date('Y', strtotime($projet->date_debut)),
                                        "valeur" => $gains_realise_N,
                                        "projet_id" => $projet->id
                                    ]);
                                    array_push($gains_achats_complexes, [
                                        "type" => "securise",
                                        "annee" => (date('Y', strtotime($projet->date_debut)) + 1),
                                        "valeur" => $gains_realise_N1,
                                        "projet_id" => $projet->id
                                    ]);
                                    GainAchatComplexeProjet::insert($gains_achats_complexes);
                                }
                            }
                        } else {
                            $this->skippedProjets++;
                        }
                    } else {
                        $this->skippedProjets++;
                    }
                }
            }
        }
    }

    public function getSuccessMessage()
    {
        $baseMessage = 'L\'import des projets s\'est bien déroulé. ' . sizeof($this->insertedProjets) . ' projet(s) importé(s), ' . $this->skippedProjets . ' projet(s) ignoré(s)';
        return $this->incoherentProjets === 0 ?
            $baseMessage . '.'
            : $baseMessage . ' et ' . $this->incoherentProjets . 'projet(s) avec des pourcentages incohérents.';
    }

    public function getErrorMessage()
    {
        return 'L\'import des projets a échoué. Veuillez réessayer en vous assurant que le fichier est pris en charge par l\'application.';
    }
}
