<?php 

namespace NPS\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use NPS\Repository\ConsultationRepository;
use Symfony\Component\HttpFoundation\Request;
use NPS\Repository\KnowledgeBaseRepository;
use NPS\Model\Consultation;
use NPS\Factory\ConsultationFactory;
use NPS\Exception\ExpertSystemCommunicationException;
use NPS\DependencyInjection\UserKnowledgeAccessGuard;
use NPS\Exception\InvalidArgumentException;
use NPS\Exception\MissingArgumentException;

/**
 * Consultation related actions controller
 * @author Lukas Korinek
 */
class ConsultationController extends AbstractController
{

    /**
     * @Route("/consultation/history")
     * @Security("is_granted('ROLE_USER')")
     */
    public function historyAction(ConsultationRepository $historyRepo, KnowledgeBaseRepository $knowledgeRepo, Request $request)
    {
        $bindId = intval($request->get('bind_id')) ?: NULL;
        $flagged = !!($request->get('flagged_only'));
        $limit = intval($request->get('limit')) ?: 25;
        $offset = intval($request->get('offset')) ?: 0;
        
        // Get ids of knowledge bases for which the search will happen
        if($bindId)
        {
            $ids = array_map(function($base) {
                return $base->getId();
            }, $knowledgeRepo->getByBindId($bindId));
        }
        else 
        {
            $ids = NULL;
        }
        
        // Briefer info to prevent waiting for NPS bridge to fetch information about all the consultations
        $consultations = $historyRepo->getConsultationHistory($this->getUser(), $offset, $limit, $ids, $flagged);
        $response = $this->response([
            'history' => array_map(function($consultation){

                return [
                    'id' => $consultation->getId(),
                    'flagged' => $consultation->isFlagged(),
                    'date_started' => $consultation->getDateStarted()->format(\DateTime::ISO8601),
                    'date_ended' => $consultation->getDateEnded() ? $consultation->getDateEnded()->format(\DateTime::ISO8601) : null,
                    'knowledge_base' => [
                        'name' => $consultation->getKnowledgeBase()->getName(),
                        'version' => $consultation->getKnowledgeBase()->getVersion()->format(\DateTime::ISO8601),
                        'status' => $consultation->getKnowledgeBase()->getStatus(),
                        'bind_id' => $consultation->getKnowledgeBase()->getKnowledgeBaseBindId()
                    ],  
                ];

            }, $consultations)
        ]);
        
        return $response;
    }

    /**
     * @Route("/consultation/start", methods={"POST"})
     */
    public function startAction(ConsultationFactory $factory, KnowledgeBaseRepository $baseRepository, UserKnowledgeAccessGuard $guard, Request $request)
    {
        if(!$baseId = $request->get('baseId'))
        {
            throw new \NPS\Exception\MissingArgumentException('baseId');
        }

        if(!$base = $baseRepository->getById($baseId))
        {
            throw new \NPS\Exception\InvalidArgumentException('baseId');
        }

        if(!$guard->hasAccess($base, $this->getUser()))
        {
            throw new \NPS\Exception\InvalidArgumentException('baseId');
        }
        
        $consultation = $factory->create($base, $this->getUser());
        $this->flush();

        return $this->response([
            'consultation' => $this->createConsultationPayload($consultation)
        ]);
    }

    /**
     * @Route("/consultation/end", methods={"POST"})
     */
    public function endAction(ConsultationRepository $repo, Request $request)
    {
        $consultation = $this->resolveConsultation($repo, $request);
        if($consultation->isEnded())
        {
            throw new InvalidArgumentException('consultationId');
        }


        $consultation->end();
        $this->flush();

        return $this->response([
            'consultation' => $consultation ? $this->createConsultationPayload($consultation) : null
        ]);
    }

    /**
     * @Route("/consultation/get")
     */
    public function getAction(ConsultationRepository $repo, Request $request)
    {
        $consultation = $this->resolveConsultation($repo, $request);
        return $this->response([
            'consultation' => $consultation ? $this->createConsultationPayload($consultation) : null
        ]);
    }

    /**
     * @Route("/consultation/answer", methods={"POST"})
     */
    public function answerAction(ConsultationRepository $repo, Request $request)
    {
        $consultation = $this->resolveConsultation($repo, $request);

        if(!$answer = intval($request->get('answerId')))
        {
            throw new \NPS\Exception\MissingArgumentException('answerId');
        }

        $consultation->setAnswer($answer);
        $this->flush();

        return $this->response([
            'consultation' => $this->createConsultationPayload($consultation)
        ]);
    }

    /**
     * @Route("/consultation/back", methods={"POST"})
     */
    public function backAction(ConsultationRepository $repo, Request $request)
    {
        $consultation = $this->resolveConsultation($repo, $request);
        $consultation->back();
        $this->flush();

        return $this->response([
            'consultation' => $this->createConsultationPayload($consultation)
        ]);
    }

    /**
     * @Route("/consultation/forward", methods={"POST"})
     */
    public function forwardAction(ConsultationRepository $repo, Request $request)
    {
        $consultation = $this->resolveConsultation($repo, $request);
        $consultation->forward();
        $this->flush();

        return $this->response([
            'consultation' => $this->createConsultationPayload($consultation)
        ]);
    }

    /**
     * @Route("/consultation/flag", methods={"POST"})
     * @Security("is_granted('ROLE_USER')")
     */
    public function toggleFlagAction(ConsultationRepository $repo, Request $request)
    {
        $consultation = $this->resolveConsultation($repo, $request);
        $consultation->toggleFlag();
        $this->flush();

        return $this->response(['flagged' => $consultation->isFlagged()]);
    }

    /**
     * Attempts to load a consultation either by request consultationId parameter or by looking for an active consultation
     * @param ConsultationRepository $repo
     * @param Request $request 
     * @return Consultation
     */
    private function resolveConsultation(ConsultationRepository $repo, Request $request)
    {
        if(!$id = intval($request->get('consultationId')))
        {
            throw new \NPS\Exception\MissingArgumentException('consultationId');
        }

        $consultation = $repo->getById($id);

        // Does the consultation really belong to the currently logged user ?
        if(!$consultation || ($this->getUser() && $consultation->getUser()->getId() != $this->getUser()->getId()))
        {
            throw new \NPS\Exception\InvalidArgumentException('consultationId');
        }

        return $consultation;
    }

    

    /**
     * Creates a payload of consultation information
     * @param Consultation $consultation 
     * @return array
     */
    private function createConsultationPayload(Consultation $consultation)
    {
        $hypothesis = $consultation->getHypothesis();
        $answers = $consultation->getAnswers();
        if(!$consultation->isEnded())
        {
            $question = $consultation->getQuestion();
        }        

        $payload = [
            'id' => $consultation->getId(),
            'date_started' => $consultation->getDateStarted()->format(\DateTime::ISO8601),
            'date_ended' => $consultation->getDateEnded() ? $consultation->getDateEnded()->format(\DateTime::ISO8601) : null,
            'username' => $consultation->getUser() ? $consultation->getUser()->getUsername() : null,
            'question' => isset($question) ? $question : null,
            'hypothesis' => $hypothesis,
            'answers' => $answers,
            'knowledge_base' => [
                'id' => $consultation->getKnowledgeBase()->getId(),
                'bind_id' => $consultation->getKnowledgeBase()->getKnowledgeBaseBindId(),
                'name' => $consultation->getKnowledgeBase()->getName(),
                'description' => $consultation->getKnowledgeBase()->getDescription(),
                'version' => $consultation->getKnowledgeBase()->getVersion()->format(\DateTime::ISO8601),
                'status' => $consultation->getKnowledgeBase()->getStatus()
            ],
        ];

        return $payload;
    }

}

