<?php

namespace App\Http\Controllers;

use App\Models\AchatComplexeProjet;
use App\Models\EtapeProjet;
use App\Models\GainAchatComplexeProjet;
use App\Models\MontantAchatComplexeProjet;
use App\Models\LevierProjet;
use App\Models\Paat;
use App\Models\Projet;
use App\Models\User;
use App\Notifications\AlerteNotification;
use APPLICATION;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use PAAT_STATUT;
use PROJET_STATUT;
use ROLE;
use TYPE_CALCUL_GAINS;

class PaatController extends Controller
{
    public function __construct()
    {
        $this->middleware('permission:see_paat')->only(['index']);
        $this->middleware('permission:add_paat')->only(['create', 'store']);
        $this->middleware('permission:edit_paat')->only(['edit', 'update', 'valider']);
        $this->middleware('permission:cloture_paat')->only(['cloturer']);
        $this->middleware('permission:delete_paat')->only(['destroy']);
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\Response
     */
    public function index()
    {
        return view('paats.index', [
            'paats' => Paat::where('structure_id', Auth::user()->structure_id)->paginate(APPLICATION::ITEMS_PER_PAGE),
            'show_add' => Paat::where('structure_id', Auth::user()->structure_id)
                                ->where('statut', PAAT_STATUT::INITIALISATION)
                                ->count() == 0
        ]);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\Response
     */
    public function create()
    {
        return view('paats.create');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function store(Request $request)
    {
        $request->validate([
            'date_debut' => 'required|date_format:d/m/Y',
            'date_fin' => 'required|date_format:d/m/Y',
            'description' => 'required|string|max:255',
            'commentaire' => 'nullable|string|max:255'
        ]);

        $paat_initialisation = Paat::where('structure_id', Auth::user()->structure_id)
                                    ->where('statut', PAAT_STATUT::INITIALISATION)->count();

        if ($paat_initialisation == 0) {
            $current_paat = Paat::where('structure_id', Auth::user()->structure_id)
                ->where('statut', PAAT_STATUT::EN_COURS)
                ->first();

            $date_debut = Carbon::createFromFormat('d/m/Y', $request->date_debut);
            $date_fin = Carbon::createFromFormat('d/m/Y', $request->date_fin);
            $new_paat = Paat::firstOrCreate([
                'description' => $request->description,
                'annee_reference' => $date_fin->year,
                'date_debut' => $date_debut,
                'date_fin' =>  $date_fin,
                'structure_id' => Auth::user()->structure_id
            ]);

            if ($new_paat->id) {
                // Si un paat est en cours pour la structure
                if (isset($current_paat)) {
                    // On le passe au statut de en cours à soumis
                    $current_paat->statut = PAAT_STATUT::SOUMIS;

                    // On passe le commentaire de statut s'il a été renseigné.
                    $current_paat->commentaire_soumission = $request->commentaire;
                    $current_paat->save();

                    // On notifie tous les directeurs achats de la clôture du PAAT
                    $message = 'Le PAAT "' . $current_paat->description . '" a été soumis pour clôture par ' . Auth::user()->nom_complet();
                    // TODO : remplacer la ligne ci-dessous par User::hasRole(ROLE::DIRECTEUR_ACHAT)->get()
                    $directeurs = User::role(ROLE::DIRECTEUR_ACHAT . '_' . Auth::user()->structure_id)->get();
                    foreach($directeurs as $directeur) {
                        $directeur->notify(new AlerteNotification($message));
                    }

                    return redirect()->route('paats.index')->with('success', 'PAAT ajouté avec succès. Le PAAT précédent à été soumis avec succès pour validation.');
                }

                return redirect()->route('paats.index')->with('success', 'PAAT ajouté avec succès.');
            } else {
                abort(500);
            }
        } else {
            abort(500);
        }
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\Response
     */
    public function show($id)
    {
        $paat = Paat::find($id);
        if (isset($paat)) {
            if ($paat->structure_id == Auth::user()->structure_id) {
                return view('paats.show', [
                    'paat' => $paat,
                    'projets' => $paat->statut === PAAT_STATUT::INITIALISATION ?
                        Projet::where('paat_id', $paat->id)->where('report', true)->paginate(APPLICATION::ITEMS_PER_PAGE) // On affiche uniquement les projets qui font parti d'un report
                        : Projet::where('paat_id', $paat->id)->paginate(APPLICATION::ITEMS_PER_PAGE) // On affiche tous les projets du PAAT
                ]);
            } else {
                abort(403);
            }
        } else {
            abort(404);
        }
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\Response
     */
    public function edit($id)
    {
        $paat = Paat::find($id);
        if (isset($paat)) {
            if ($paat->structure_id == Auth::user()->structure_id
                && $paat->statut != PAAT_STATUT::CLOTURE) {
                return view('paats.edit', ['paat' => $paat]);
            } else {
                abort(403);
            }
        } else {
            abort(404);
        }
    }

    /**
     * Permet de valider un paat en cours d'initialisation
     *
     * @param int $id
     * @return array|\Illuminate\Http\RedirectResponse
     */
    public function valider(int $id)
    {
        $paat_en_cours = Paat::where('structure_id', Auth::user()->structure_id)
            ->where('statut', PAAT_STATUT::EN_COURS)->count();

        if ($paat_en_cours == 0) {
            $paat = Paat::find($id);
            if (isset($paat)) {
                if ($paat->structure_id == Auth::user()->structure_id
                    && $paat->statut == PAAT_STATUT::INITIALISATION) {
                    $paat->update(['statut' => PAAT_STATUT::EN_COURS]);
                    return redirect()->route('paats.index')->with('success', 'Le PAAT a été créé avec succès.');
                } else {
                    abort(403);
                }
            } else {
                abort(404);
            }
        } else {
            abort(500);
        }
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function update(Request $request, $id)
    {
        $request->validate([
            'date_debut' => 'required|date_format:d/m/Y',
            'date_fin' => 'required|date_format:d/m/Y',
            'description' => 'required|string|max:255'
        ]);

        $paat = Paat::find($id);
        if (isset($paat)) {
            // On ne peut update qu'un PAAT qui n'est pas cloturé
            if ($paat->structure_id == Auth::user()->structure_id
                && $paat->statut != PAAT_STATUT::CLOTURE) {
                $date_debut = Carbon::createFromFormat('d/m/Y', $request->date_debut);
                $date_fin = Carbon::createFromFormat('d/m/Y', $request->date_fin);
                $paat->update([
                    'date_debut' => $date_debut,
                    'date_fin' => $date_fin,
                    'annee_reference' => $date_fin->year,
                    'description' => $request->description
                ]);
                return redirect()->route('paats.index')->with('success', 'PAAT mis à jour avec succès.');
            } else {
                abort(403);
            }
        } else {
            abort(404);
        }
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function destroy($id)
    {
        $paat = Paat::find($id);
        if (isset($paat)) {
            if ($paat->structure_id == Auth::user()->structure_id
                && $paat->statut == PAAT_STATUT::INITIALISATION) {
                // Si le PAAT contient un projet qui a été étudié par une personne on peut pas le supprimer -> Cf ProjetController
                // On récupère ne nombre de projet qui possède au moins un statut différent de en_cours == étudié par une personne
                $status_count = Projet::join('projets_statuts', 'projets_statuts.projet_id', '=', 'projets.id')
                    ->where('paat_id', $paat->id)->where('projets_statuts.statut_id', '!=', PROJET_STATUT::EN_COURS)->count();
                if ($status_count > 0) {
                    return redirect()->back()->withErrors('Impossible de supprimer le PAAT car un ou plusieurs projets associés ont fait l\'objet d\'une étude par des utilisateurs.');
                }

                //On delete tous les projets du paat => Pas d'importance car projets dupliqués (CF Store)
                $projets = Projet::where('paat_id', $paat->id)->get();
                foreach ($projets as $p) {
                    $p->delete();
                }
                $paat->delete();
                return redirect()->route('paats.index')->with('success', 'PAAT supprimé avec succès.');
            } else {
                abort(403);
            }
        } else {
            abort(404);
        }
    }

    /**
     * Supprime un projet du paat
     *
     * @param  int  $paat_id
     * @param  int  $projet_id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function delete_projet($paat_id, $projet_id)
    {
        $projet = Projet::find($projet_id);
        if (isset($projet)) {
            if (Projet::checkIfUserCanEditOrDestroy($projet)) {
                if ($projet->statuts->where('statut_id', '!=', PROJET_STATUT::EN_COURS)->count() > 0) {
                    return redirect()->back()->withErrors('Impossible de supprimer le projet car il a déjà été étudié par d\'autres utilisateurs.');
                }

                $projet->delete();
                return redirect()->route('paats.show', $paat_id)->with('success', 'Le projet ne sera pas reporté.');
            } else {
                abort(403);
            }
        } else {
            abort(404);
        }
    }

    /**
     * Permet de cloturer un paat
     * @param int $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function cloturer(int $id)
    {
        $paat = Paat::find($id);
        if (isset($paat)) {
            if ($paat->structure_id == Auth::user()->structure_id
                && $paat->statut == PAAT_STATUT::SOUMIS) {

                $new_paat = Paat::where('structure_id', Auth::user()->structure_id)
                ->where('annee_reference', $paat->annee_reference + 1)
                ->first();

                $insert = true;

                // S'il y a un nouveau PAAT de créé
                if ($new_paat != null) {
                    // On récupère tous les projets du paat qui ne seront pas terminés avant la date de fin
                    $projets_non_termines = Projet::where('paat_id', $paat->id)->where('structure_id', Auth::user()->structure_id)
                        ->where(function ($query) use ($paat) {
                            $query->whereNull('date_mise_en_oeuvre')
                            ->orWhereRaw('TIMESTAMPDIFF(MONTH, date_mise_en_oeuvre, \'' . $paat->date_fin . '\') < duree_mois');
                        })->get();

                    // On duplique tous les projets
                    foreach ($projets_non_termines as $p) {
                        $tmp = $p->replicate();
                        // On set le booleen de report à true
                        $tmp->report = true;
                        // On associe le projet dupliqué au nouveau paat
                        $tmp->paat_id = $new_paat->id;
                        // On change les dates prévisionnelles de début et de fin
                        // EX : Si le projet a commencé en novembre 2020 et fait l'objet d'un report sur 2021 -> le 01/01/2021 il reste la durée en mois du projet - les 2 mois de 2020 deja écoulé
                        $old_paat_date_fin = Carbon::createFromFormat('Y-m-d', $paat->date_fin);
                        $old_date_debut = Carbon::createFromFormat('Y-m-d', $tmp->date_debut);
                        $old_date_fin = Carbon::createFromFormat('Y-m-d', $tmp->date_fin);
                        $tmp->date_debut = Carbon::createFromFormat('Y-m-d', $new_paat->date_debut);
                        $tmp->date_fin = Carbon::createFromFormat('Y-m-d', $new_paat->date_debut)->addMonths(($tmp->duree_mois ?? 12) - $old_paat_date_fin->diffInMonths($old_date_debut));
                        $insert = $tmp->push();

                        // On duplique également toutes les relations
                        $p->relations = [];
                        $p->load('leviers_projets', 'membres_projets', 'statuts', 'montants_projets', 'actions_resp_soc_env', 'achats_complexes_projets', 'gains_projets', 'taches', 'etapes_projets', 'fichiers');

                        // On récupère le dernier statut du projet
                        $last_status = $p->statut()->first();
                        foreach($p->getRelations() as $relation => $items) {
                            // Pour les status, on ajout tout d'un coup sauf le dernier pour maitriser le champs created_at
                            // Permet d'afficher le bon icon dans l'onglet synthese des actions achats des projets
                            if ($relation === 'statuts') {
                                foreach($items as $item) {
                                    unset($item->id);
                                    $date = $last_status->id === $item->statut_id ? Carbon::now()->addSeconds(1) : Carbon::now();
                                    $item->created_at = $date;
                                    $item->updated_at = $date;
                                    $tmp->{$relation}()->create($item->toArray());
                                }
                            } else {
                                // Dans tous les autres cas on insére les éléments
                                foreach($items as $item) {
                                    unset($item->id);
                                    $tmp->{$relation}()->create($item->toArray());
                                }
                            }
                        }

                        // On reporte les gains du projet en décrémentant les années
                        $this->reporter_gains_projet($tmp);

                        // Si un insert s'est mal passé, on arrete l'itération
                        if (!$insert) {
                            break;
                        }
                    }
                }

                if ($insert) {
                    $paat->update(['statut' => PAAT_STATUT::CLOTURE]);
                    // On notifie tous les CdP de la clôture du PAAT
                    $message = 'Le PAAT "' . $paat->description . '" a été clôturé par ' . Auth::user()->nom_complet();
                    $cdps = User::role(ROLE::CHEF_PROJET . '_' . Auth::user()->structure_id)->get();
                    foreach($cdps as $cdp) {
                        $cdp->notify(new AlerteNotification($message));
                    }

                    return redirect()->route('paats.index')->with('success', 'PAAT cloturé avec succès.');
                } else {
                    // On supprime les projets qui ont été dupliqués pour revenir à l'état précédent
                    Projet::where('paat_id', $new_paat->id)->delete();
                    $new_paat->delete();
                    return redirect()->route('paats.index')->withErrors('Une erreur est survenue. Impossible de transfére les projets sur le nouveau PAAT.');
                }
            } else {
                abort(403);
            }
        } else {
            abort(404);
        }
    }


    /**
     * Permet de reporter les gains d'un projet en fonction de son type
     */
    private function reporter_gains_projet($new_projet)
    {

        switch($new_projet->type_calcul_gains) {
            // Pour les achats simples, on reporte les gains de l'année N+1 sur l'année N
            // puis on met à 0 les gains de l'année N+1
            case TYPE_CALCUL_GAINS::ACHATS_RECURRENTS:
            case TYPE_CALCUL_GAINS::ACHATS_INVESTISSEMENT:
                // $anneeN = date('Y', strtotime($new_projet->date_debut));
                $anneeN = $new_projet->date_debut->toImmutable()->subYears(1)->year;
                GainAchatComplexeProjet::where('projet_id', $new_projet->id)->where('annee', $anneeN)->where('type', 'cible')->delete();
                // A garder pour l'instant
                /*GainAchatComplexeProjet::insert([
                    "type" => "cible",
                    "annee" => $anneeN + 2,
                    "valeur" => 0,
                    "projet_id" => $new_projet->id
                ]);*/

                // S'il y a une date de notification, on met à jour également les gains notifiés et réalisés
                if ($new_projet->date_mise_en_oeuvre != null) {
                    GainAchatComplexeProjet::where('projet_id', $new_projet->id)->where('annee', $anneeN)->where('type', 'notifie')->delete();
                    GainAchatComplexeProjet::where('projet_id', $new_projet->id)->where('annee', $anneeN)->where('type', 'securise')->delete();
                }

                break;

                // Pour les achats complexes, on ne garde que la première année pour l'ancien projet
                // puis on reporte les années suivantes, sauf la première, sur le nouveau projet
            case TYPE_CALCUL_GAINS::ACHATS_COMPLEXES:
                $nbAnnees = 0;
                $firstAnnee = 0;
                $achats_complexes = [];
                foreach($new_projet->achats_complexes() as $annee => $row) {
                    $achats_complexes[$annee] = [];
                    foreach($row as $valeur) {
                        $achats_complexes[$annee][$valeur->type] = $valeur->valeur;
                    }
                    if ($nbAnnees === 0) {
                        $firstAnnee = $annee;
                    }
                    $nbAnnees++;
                }

                if ($nbAnnees === 1) {
                    AchatComplexeProjet::where('projet_id', $new_projet->id)->delete();
                } elseif ($nbAnnees > 1) {
                    // On supprime la première colonne du nouveau projet et on réduit sa durée
                    AchatComplexeProjet::where('projet_id', $new_projet->id)->where('annee', $firstAnnee)->delete();
                    GainAchatComplexeProjet::where('projet_id', $new_projet->id)->where('annee', $firstAnnee)->delete();
                    MontantAchatComplexeProjet::where('projet_id', $new_projet->id)->where('annee', $firstAnnee)->delete();
                    Projet::where('id', $new_projet->id)->update(["duree_mois" => $new_projet->duree_mois - 12]);
                }
                break;
        }
    }
}
