<?php
/**
 * DocumentTypeController.php.
 *
 * Created By: jonathan
 * Date: 15/10/2020
 * Time: 12:59
 */

namespace App\Http\Controllers\Api;

use App\Exceptions\MissingMaturityAnswerException;
use App\Http\Controllers\Controller;
use App\Http\Requests\EvaluationRequest;
use App\Models\Danger;
use App\Models\DangerLevelEvaluation;
use App\Models\Evaluation;
use App\Models\EvaluationMaturityAnswer;
use App\Models\Measure;
use App\Models\MeasureLevel;
use App\Models\User;
use App\Repository\EvaluationRepository;
use App\Repository\GraphDataRepository;
use Illuminate\Support\Facades\Auth;

class EvaluationsController extends Controller
{
    public function __construct(GraphDataRepository $repository)
    {
        $this->repository = $repository;
    }

    public function all()
    {
        $user = Auth::user();

        if (User::ROLE_ADMIN === $user->role) {
            return Evaluation::with('dangerLevels')
                ->with('measureLevels')
                ->with('maturityLevels')
                ->with('organization')
                ->get();
        }

        return Evaluation::where('organization_id', $user->organization_id)
                ->with('dangerLevels')
                ->with('measureLevels')
                ->with('maturityLevels')
                ->with('organization')
                ->get();
    }

    public function get(int $id)
    {
        $user = Auth::user();

        $evaluation = Evaluation::find($id);

        // if user is admin or if user->organization = $id
        if ((User::ROLE_ADMIN === $user->role) || (($user->role > User::ROLE_ADMIN) && ($user->organization_id === $evaluation->organization_id))) {
            return Evaluation::where('id', $id)
                ->with('dangerLevels')
                ->with('measureLevels')
                ->with('maturityLevels')
                ->with('organization')
                ->get();
        }

        abort(403);
    }

    /**
     * Enregistre l'évaluation en tant que terminée (validation sur l'étape en cours).
     *
     * @return Evaluation|null
     */
    public function save(EvaluationRepository $repository, EvaluationRequest $request, $id = null)
    {
        $user = Auth::user();
        $data = $request->validated();

        if ($user->role <= User::ROLE_MANAGER) {
            if ((User::ROLE_MANAGER === $user->role) && ($data['organization_id'] !== $user->organization_id)) {
                abort(403);
            }
            if (!isset($id)) {
                $evaluation = new Evaluation();
                $evaluation->author = $user->firstname . ' ' . $user->lastname;
                $evaluation->current_step = 1;
            } else {
                $evaluation = Evaluation::find($id);
                if ($evaluation->reference !== env('REFERENTIEL_VERSION')) {
                    abort(422, 'Le référentiel de cette évaluation ne correspond pas au référentiel actuel');
                }
            }

            $evaluation->status = $data['status'];
            $evaluation->updated_by = $user->firstname . ' ' . $user->lastname;
            $evaluation->organization_id = $data['organization_id'];
            $evaluation->reference = env('REFERENTIEL_VERSION');
            $evaluation->save();

            // Evaluer les dangers
            if (Evaluation::STEP_DANGERS === intval($evaluation->current_step) && isset($data['danger_levels'])) {
                $dangerLevelArray = [];
                $dangers = Danger::all();
                $levels = collect($data['danger_levels']);

                foreach ($dangers as $danger) {
                    $level = $levels->filter(function ($l) use ($danger) {
                        return isset($l['danger_id']) && $l['danger_id'] === $danger->id;
                    })->first();

                    if ((!$level || !isset($level['danger_level_id'])) && 0 === $data['draft']) {
                        abort(422, 'Un ou plusieurs niveaux de dangers sont manquants');
                    }
                    if ($level && isset($level['danger_level_id'])) {
                        $dl = new DangerLevelEvaluation();
                        $dl->danger_id = $danger->id;
                        $dl->danger_level_id = $level['danger_level_id'];

                        $dangerLevelArray[] = $dl;
                    }
                }
                $evaluation->dangerLevels()->delete();
                $evaluation->dangerLevels()->saveMany($dangerLevelArray);
            }

            // Evaluer les mesures d'évaluation
            if ((Evaluation::STEP_MEASURES === intval($evaluation->current_step)) || (Evaluation::STEP_ACTIONS === intval($evaluation->current_step))) {
                // get list of actuals measures
                $previous_measures_array = MeasureLevel::where('evaluation_id', $evaluation['id'])->pluck('id')->toArray();
                foreach ($data['measure_levels'] as $put_measure_level) {
                    $measure = Measure::find($put_measure_level['measure_id']);

                    if (isset($put_measure_level['id'])) {
                        $measure_level = MeasureLevel::find($put_measure_level['id']);
                    } else {
                        $measure_level = new MeasureLevel();
                    }

                    $measure_level->measure_id = $put_measure_level['measure_id'];
                    $measure_level->evaluation_id = $evaluation['id'];
                    $measure_level->actual_level = $put_measure_level['actual_level'] ?? null;
                    $measure_level->expected_level = $put_measure_level['expected_level'] ?? null;
                    $measure_level->manager = $put_measure_level['manager'] ?? null;
                    $measure_level->end_date = $put_measure_level['end_date'] ?? null;
                    $measure_level->save();

                    if (in_array($measure_level['id'], $previous_measures_array)) {
                        $previous_measures_array = array_diff($previous_measures_array, [$measure_level['id']]);
                    }
                }

                foreach ($previous_measures_array as $previous_measure) {
                    MeasureLevel::where('id', $previous_measure)
                        ->delete();
                }
            }

            // Evaluer la maturité cyber
            if (Evaluation::STEP_MATURITY === intval($data['current_step'])) {
                if (!isset($data['maturity_levels'])) {
                    abort(422, 'Les réponses aux questions de maturité sont obligatoires.');
                }

                $answers = collect($data['maturity_levels']);
                try {
                    $repository->saveMaturity($evaluation, $answers, $data['draft']);
                } catch (MissingMaturityAnswerException $e) {
                    abort(422, 'Certaines réponses aux questions de maturité sont manquantes.');
                }
            }

            if (!isset($data['draft']) || !$data['draft']) {
                // Change step if this is not a draft evaluation
                ++$evaluation->current_step;
            } else {
                // Get step from front end if it is a draft evaluation
                $evaluation->current_step = $data['current_step'];
            }

            // Make sure step does not go above 6
            if ($evaluation->current_step >= 6) {
                $evaluation->current_step = 6;
            }

            // Set status done if step is 6
            if ($evaluation->current_step >= 6) {
                $evaluation->status = Evaluation::STATUS_DONE;
            } else {
                $evaluation->status = Evaluation::STATUS_ONGOING;
            }

            $evaluation->save();

            // Ajout du niveau supérieur pour les mesures preconisées
            $preconizedMeasure = array_slice($this->repository->getBestMeasuresForEvaluation($evaluation),0,4);
            foreach ($evaluation->measureLevels as $measureLevel) {
                $measureLevelpreconized = MeasureLevel::whereId($measureLevel->id)
                    ->first();
                if ($measureLevelpreconized && in_array($measureLevelpreconized['measure_id'], $preconizedMeasure, true)) {
                    $measureLevelpreconized->expected_level = $measureLevelpreconized['actual_level'] < 3 ? $measureLevelpreconized['actual_level'] + 1 : null;
                }
                $measureLevelpreconized->save();
            }

            // si mofification d'un step, reinitialisation des steps suivants
            if (isset($data['draft']) && $data['draft']) {
                if (1 === $evaluation->current_step) {
                    $this->deleteMeasureOrActionPlanStep($evaluation, 1);
                }
                if (2 === $evaluation->current_step) {
                    $this->deleteMeasureOrActionPlanStep($evaluation, 2);
                }
                if (4 === $evaluation->current_step) {
                    $this->deleteMaturityStep($evaluation);
                }
            }

            return Evaluation::where('id', $evaluation->id)
                ->with('dangerLevels')
                ->with('measureLevels')
                ->with('organization')
                ->with('maturityLevels')
                ->first();
        }

        abort(403);
    }

    private function deleteMeasureOrActionPlanStep($evaluation, $step)
    {
        $measureLevels = MeasureLevel::where('evaluation_id', $evaluation->id)
            ->get();

        foreach ($measureLevels as $mesureLevel) {
            $measure = Measure::find($mesureLevel->measure_id);
            if (1 === $step) {
                $mesureLevel->actual_level = null;
                $mesureLevel->expected_level = null;
            }
            if (2 === $step) {
                $mesureLevel->expected_level = null;
            }

            $mesureLevel->end_date = null;
            $mesureLevel->manager = null;
            $mesureLevel->save();
        }

        $this->deleteMaturityStep($evaluation);
    }

    private function deleteMaturityStep($evaluation)
    {
        EvaluationMaturityAnswer::where('evaluation_id', $evaluation->id)
            ->delete();
    }

    public function delete(int $id = null)
    {
        $user = Auth::user();

        $evaluation = Evaluation::find($id);

        if ((User::ROLE_ADMIN === $user->role) || ((User::ROLE_MANAGER === $user->role) && ($user->organization_id === $evaluation->organization_id))) {
            $evaluation->delete();

            return abort(204);
        }

        abort(403);
    }
}