<?php 

namespace NPS\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use NPS\DependencyInjection\KnowledgeBaseManager;
use NPS\Repository\KnowledgeBaseRepository;
use Symfony\Component\HttpFoundation\Request;
use NPS\DependencyInjection\UserKnowledgeAccessGuard;
use NPS\Repository\UserRepository;
use NPS\Exception\InvalidArgumentException;
use NPS\Exception\MissingArgumentException;

/**
 * Controller for knowledge base operations
 * @author Lukas Korinek
 */
class KnowledgeController extends AbstractController
{

    /**
     * Lists knowledge bases visible for the user
     * @Route("/knowledge")
     */ 
    public function knowledgeAction(KnowledgeBaseRepository $repository, UserKnowledgeAccessGuard $guard, Request $request)
    { 
        // Get all the accessible bases
        $bases = $guard->filterAccessible($repository->getAll(), $this->getUser());
        
        // Group the knowledge bases by name
        $grouped = array_reduce($bases, function($acc, $base)
        {
            $acc[$base->getKnowledgeBaseBindId()][] = $base;
            return $acc;
        }, []);

        $bases = [];
        foreach($grouped as $name => $versions)
        {
            $base = reset($versions);
            
            $bases[] = [
                'name' => $base->getName(),
                'description' => $base->getDescription(),
                'id' => $base->getId(),
                'bind_id' => $base->getKnowledgeBaseBindId(),
                'status' => $base->getStatus(),
                'date' => $base->getVersion()->format(\DateTime::ISO8601),
                'languages' => $base->getSupportedLanguages(),
                'versions' => array_map(function($version){
                    
                    return [
                        'id' => $version->getId(),
                        'date' => $version->getVersion()->format(\DateTime::ISO8601),
                        'status' => $version->getStatus()
                    ];

                }, $versions)
            ];
        }

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

    /**
     * Grants access to a knowledge base for a group of users or an individual user
     * @Security("is_granted('ROLE_MANAGER') or is_granted('ROLE_ADMIN')")
     * @Route("/knowledge/grant", methods={"POST"})
     */
    public function grantAccessAction(KnowledgeBaseRepository $knowledgeRepo, UserRepository $userRepo, Request $request)
    {
        if(!($bindId = $request->get('bind_id')))
        {
            throw new MissingArgumentException('bind_id');
        }

        if(!$knowledgeRepo->validateBindId($bindId))
        {
            throw new InvalidArgumentException('bind_id');
        }

        if(empty($ids = $request->get('users', [])))
        {
            throw new MissingArgumentException('users');
        }

        $users = array_map(function($id) use ($userRepo) 
        {
            $user = $userRepo->getById($id);
            if(!$user)
            {
                throw new InvalidArgumentException('users');
            }

            return $user;

        }, $ids);

        foreach($users as $user)
        {
            $user->grantAccess($bindId);
        }
        
        $this->flush();

        return $this->response(['grants' => $user->getGrants()]);
    }

    
    /**
     * Denies access to a knowledge base for a group of users or an individual user
     * @Security("is_granted('ROLE_MANAGER') or is_granted('ROLE_ADMIN')")
     * @Route("/knowledge/deny", methods={"POST"})
     */
    public function denyAccessAction(KnowledgeBaseRepository $knowledgeRepo, UserRepository $userRepo, Request $request)
    {
        if(!($bindId = $request->get('bind_id')))
        {
            throw new MissingArgumentException('bind_id');
        }

        if(!$knowledgeRepo->validateBindId($bindId))
        {
            throw new InvalidArgumentException('bind_id');
        }

        if(empty($ids = $request->get('users', [])))
        {
            throw new MissingArgumentException('users');
        }

        $users = array_map(function($id) use ($userRepo) 
        {
            $user = $userRepo->getById($id);
            if(!$user)
            {
                throw new InvalidArgumentException('users');
            }

            return $user;

        }, $ids);

        foreach($users as $user)
        {
            $user->denyAccess($bindId);
        }
        
        $this->flush();

        return $this->response(['grants' => $user->getGrants()]);
    }

    /**
     * Grants access to knowledge bases per user
     * @Security("is_granted('ROLE_MANAGER') or is_granted('ROLE_ADMIN')")
     * @Route("/knowledge/grants", methods={"POST"})
     */
    public function grantsAction(KnowledgeBaseRepository $knowledgeRepo, UserRepository $userRepo, Request $request)
    {
        $id = $request->get('userId');
        if(!$id)
        {
            throw new MissingArgumentException('userId');
        }

        /** @var User $user */
        $user = $userRepo->getById($id);
        if(!$user)
        {
            throw new InvalidArgumentException('userId');
        }

        // Admin user override safeguard
        if(!$this->isGranted('ROLE_ADMIN') && (in_array('ROLE_ADMIN', $user->getRoles()) || in_array('ROLE_MANAGER', $user->getRoles())))
        {
            throw new InvalidArgumentException('userId');
        }

        $bindIds = $request->get('bindIds', []);
        if(!empty($bindIds) && !is_array($bindIds))
        {
            throw new InvalidArgumentException('bindIds');
        }

        foreach($bindIds as $bindId)
        {
            if(!$knowledgeRepo->validateBindId($bindId))
            {
                throw new InvalidArgumentException('bindIds');
            }
        }

        foreach($user->getGrants() as $bindId)
        {
            $user->denyAccess($bindId);
        }

        foreach($bindIds as $bindId)
        {
            $user->grantAccess($bindId);
        }
        
        $this->flush();

        return $this->response(['grants' => $user->getGrants()]);
    }

}

