<?php

/*
 * This file is part of Phraseanet
 *
 * (c) 2005-2014 Alchemy
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Alchemy\Phrasea\Controller\Prod;

use Silex\Application;
use Silex\ControllerProviderInterface;
use Alchemy\Phrasea\Helper\Record as RecordHelper;
use Alchemy\Phrasea\Controller\Exception as ControllerException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 *
 * @license     http://opensource.org/licenses/gpl-3.0 GPLv3
 * @link        www.phraseanet.com
 */
class Push implements ControllerProviderInterface
{

    protected function getUserFormatter()
    {
        return function (\User_Adapter $user) {
            $subtitle = array_filter(array($user->get_position(), $user->get_company()));

            return array(
                'type'         => 'USER'
                , 'usr_id'       => $user->get_id()
                , 'firstname'    => $user->get_firstname()
                , 'lastname'     => $user->get_lastname()
                , 'email'        => $user->get_email()
                , 'display_name' => $user->get_display_name()
                , 'subtitle'     => implode(', ', $subtitle)
            );
        };
    }

    protected function getListFormatter($app)
    {
        $userFormatter = $this->getUserFormatter();

        return function (\Entities\UsrList $List) use ($userFormatter, $app) {
            $entries = array();

            foreach ($List->getEntries() as $entry) {
                /* @var $entry \Entities\UsrListEntry */
                $entries[] = array(
                    'Id'   => $entry->getId(),
                    'User' => $userFormatter($entry->getUser($app))
                );
            }

            return array(
                'type'    => 'LIST'
                , 'list_id' => $List->getId()
                , 'name'    => $List->getName()
                , 'length'  => count($entries)
                , 'entries' => $entries
            );
        };
    }

    protected function getUsersInSelectionExtractor()
    {
        return function (array $selection) {
            $Users = new \Doctrine\Common\Collections\ArrayCollection();

            foreach ($selection as $record) {
                /* @var $record record_adapter */
                foreach ($record->get_caption()->get_fields() as $caption_field) {
                    foreach ($caption_field->get_values() as $value) {
                        if (!$value->getVocabularyType())
                            continue;

                        if ($value->getVocabularyType()->getType() !== 'User')
                            continue;

                        $user = $value->getRessource();

                        $Users->set($user->get_id(), $user);
                    }
                }
            }

            return $Users;
        };
    }

    public function connect(Application $app)
    {
        $controllers = $app['controllers_factory'];

        $app['firewall']->addMandatoryAuthentication($controllers);

        $controllers->before(function (Request $request) use ($app) {
            $app['firewall']->requireRight('push');
        });

        $userFormatter = $this->getUserFormatter();

        $listFormatter = $this->getListFormatter($app);

        $userSelection = $this->getUsersInSelectionExtractor();

        $controllers->post('/sendform/', function (Application $app) use ($userSelection) {
            $push = new RecordHelper\Push($app, $app['request']);

            $repository = $app['EM']->getRepository('\Entities\UsrList');

            $RecommendedUsers = $userSelection($push->get_elements());

            $params = array(
                'push'             => $push,
                'message'          => '',
                'lists'            => $repository->findUserLists($app['authentication']->getUser()),
                'context'          => 'Push',
                'RecommendedUsers' => $RecommendedUsers
            );

            return $app['twig']->render('prod/actions/Push.html.twig', $params);
        });

        $controllers->post('/validateform/', function (Application $app) use ($userSelection) {
            $push = new RecordHelper\Push($app, $app['request']);

            $repository = $app['EM']->getRepository('\Entities\UsrList');

            $RecommendedUsers = $userSelection($push->get_elements());

            $params = array(
                'push'             => $push,
                'message'          => '',
                'lists'            => $repository->findUserLists($app['authentication']->getUser()),
                'context'          => 'Feedback',
                'RecommendedUsers' => $RecommendedUsers
            );

            return $app['twig']->render('prod/actions/Push.html.twig', $params);
        });

        $controllers->post('/send/', function (Application $app) {
            $request = $app['request'];

            $ret = array(
                'success' => false,
                'message' => _('Unable to send the documents')
            );

            try {
                $pusher = new RecordHelper\Push($app, $app['request']);

                $push_name = $request->request->get('name', sprintf(_('Push from %s'), $app['authentication']->getUser()->get_display_name()));
                $push_description = $request->request->get('push_description');

                $receivers = $request->request->get('participants');

                if (!is_array($receivers) || count($receivers) === 0) {
                    throw new ControllerException(_('No receivers specified'));
                }

                if (!is_array($pusher->get_elements()) || count($pusher->get_elements()) === 0) {
                    throw new ControllerException(_('No elements to push'));
                }

                foreach ($receivers as $receiver) {
                    try {
                        $user_receiver = \User_Adapter::getInstance($receiver['usr_id'], $app);
                    } catch (\Exception $e) {
                        throw new ControllerException(sprintf(_('Unknown user %d'), $receiver['usr_id']));
                    }

                    $Basket = new \Entities\Basket();
                    $Basket->setName($push_name);
                    $Basket->setDescription($push_description);
                    $Basket->setOwner($user_receiver);
                    $Basket->setPusher($app['authentication']->getUser());
                    $Basket->setIsRead(false);

                    $app['EM']->persist($Basket);

                    foreach ($pusher->get_elements() as $element) {
                        $BasketElement = new \Entities\BasketElement();
                        $BasketElement->setRecord($element);
                        $BasketElement->setBasket($Basket);

                        $app['EM']->persist($BasketElement);

                        $Basket->addElement($BasketElement);

                        if ($receiver['HD']) {
                            $user_receiver->ACL()->grant_hd_on(
                                $BasketElement->getRecord($app)
                                , $app['authentication']->getUser()
                                , \ACL::GRANT_ACTION_PUSH
                            );
                        } else {
                            $user_receiver->ACL()->grant_preview_on(
                                $BasketElement->getRecord($app)
                                , $app['authentication']->getUser()
                                , \ACL::GRANT_ACTION_PUSH
                            );
                        }
                    }

                    $app['EM']->flush();

                    $arguments = array(
                        'ssel_id' => $Basket->getId(),
                    );

                    if (!$app['phraseanet.registry']->get('GV_enable_push_authentication') || !$request->get('force_authentication')) {
                        $arguments['LOG'] = $app['tokens']->getUrlToken(
                            \random::TYPE_VIEW,
                            $user_receiver->get_id(),
                            null,
                            $Basket->getId()
                        );
                    }

                    $url = $app->url('lightbox_compare', $arguments);

                    $receipt = $request->get('recept') ? $app['authentication']->getUser()->get_email() : '';

                    $params = array(
                        'from'       => $app['authentication']->getUser()->get_id()
                        , 'from_email' => $app['authentication']->getUser()->get_email()
                        , 'to'         => $user_receiver->get_id()
                        , 'to_email'   => $user_receiver->get_email()
                        , 'to_name'    => $user_receiver->get_display_name()
                        , 'url'        => $url
                        , 'accuse'     => $receipt
                        , 'message'    => $request->request->get('message')
                        , 'ssel_id'    => $Basket->getId()
                    );

                    $app['events-manager']->trigger('__PUSH_DATAS__', $params);
                }

                $app['phraseanet.logger']($BasketElement->getRecord($app)->get_databox())
                    ->log($BasketElement->getRecord($app), \Session_Logger::EVENT_VALIDATE, $user_receiver->get_id(), '');

                $app['EM']->flush();

                $message = sprintf(
                    _('%1$d records have been sent to %2$d users')
                    , count($pusher->get_elements())
                    , count($receivers)
                );

                $ret = array(
                    'success' => true,
                    'message' => $message
                );
            } catch (ControllerException $e) {
                $ret['message'] = $e->getMessage() . $e->getFile() . $e->getLine();
            }

            return $app->json($ret);
        })->bind('prod_push_send');

        $controllers->post('/validate/', function (Application $app) {
            $request = $app['request'];

            $ret = array(
                'success' => false,
                'message' => _('Unable to send the documents')
            );

            $app['EM']->beginTransaction();

            try {
                $pusher = new RecordHelper\Push($app, $app['request']);

                $repository = $app['EM']->getRepository('\Entities\Basket');

                $validation_name = $request->request->get('name', sprintf(_('Validation from %s'), $app['authentication']->getUser()->get_display_name()));
                $validation_description = $request->request->get('validation_description');

                $participants = $request->request->get('participants');

                if (!is_array($participants) || count($participants) === 0) {
                    throw new ControllerException(_('No participants specified'));
                }

                if (!is_array($pusher->get_elements()) || count($pusher->get_elements()) === 0) {
                    throw new ControllerException(_('No elements to validate'));
                }

                if ($pusher->is_basket()) {
                    $Basket = $pusher->get_original_basket();
                } else {
                    $Basket = new \Entities\Basket();
                    $Basket->setName($validation_name);
                    $Basket->setDescription($validation_description);
                    $Basket->setOwner($app['authentication']->getUser());
                    $Basket->setIsRead(false);

                    $app['EM']->persist($Basket);

                    foreach ($pusher->get_elements() as $element) {
                        $BasketElement = new \Entities\BasketElement();
                        $BasketElement->setRecord($element);
                        $BasketElement->setBasket($Basket);

                        $app['EM']->persist($BasketElement);

                        $Basket->addElement($BasketElement);
                    }
                    $app['EM']->flush();
                }

                $app['EM']->refresh($Basket);

                if (!$Basket->getValidation()) {
                    $Validation = new \Entities\ValidationSession();
                    $Validation->setInitiator($app['authentication']->getUser());
                    $Validation->setBasket($Basket);

                    $duration = (int) $request->request->get('duration');

                    if ($duration > 0) {
                        $date = new \DateTime('+' . $duration . ' day' . ($duration > 1 ? 's' : ''));
                        $Validation->setExpires($date);
                    }

                    $Basket->setValidation($Validation);
                    $app['EM']->persist($Validation);
                } else {
                    $Validation = $Basket->getValidation();
                }

                $found = false;
                foreach ($participants as $key => $participant) {
                    if ($participant['usr_id'] == $app['authentication']->getUser()->get_id()) {
                        $found = true;
                        break;
                    }
                }

                if (!$found) {
                    $participants[$app['authentication']->getUser()->get_id()] = array(
                        'see_others' => 1,
                        'usr_id'     => $app['authentication']->getUser()->get_id(),
                        'agree'      => 0,
                        'HD'         => 0
                    );
                }

                foreach ($participants as $key => $participant) {
                    foreach (array('see_others', 'usr_id', 'agree', 'HD') as $mandatoryparam) {
                        if (!array_key_exists($mandatoryparam, $participant))
                            throw new ControllerException(sprintf(_('Missing mandatory parameter %s'), $mandatoryparam));
                    }

                    try {
                        $participant_user = \User_Adapter::getInstance($participant['usr_id'], $app);
                    } catch (\Exception $e) {
                        throw new ControllerException(sprintf(_('Unknown user %d'), $participant['usr_id']));
                    }

                    try {
                        $Participant = $Validation->getParticipant($participant_user, $app);
                        continue;
                    } catch (NotFoundHttpException $e) {

                    }

                    $Participant = new \Entities\ValidationParticipant();
                    $Participant->setUser($participant_user);
                    $Participant->setSession($Validation);

                    $Participant->setCanAgree($participant['agree']);
                    $Participant->setCanSeeOthers($participant['see_others']);

                    $app['EM']->persist($Participant);

                    foreach ($Basket->getElements() as $BasketElement) {
                        $ValidationData = new \Entities\ValidationData();
                        $ValidationData->setParticipant($Participant);
                        $ValidationData->setBasketElement($BasketElement);
                        $BasketElement->addValidationData($ValidationData);

                        if ($participant['HD']) {
                            $participant_user->ACL()->grant_hd_on(
                                $BasketElement->getRecord($app)
                                , $app['authentication']->getUser()
                                , \ACL::GRANT_ACTION_VALIDATE
                            );
                        } else {
                            $participant_user->ACL()->grant_preview_on(
                                $BasketElement->getRecord($app)
                                , $app['authentication']->getUser()
                                , \ACL::GRANT_ACTION_VALIDATE
                            );
                        }

                        $app['EM']->merge($BasketElement);
                        $app['EM']->persist($ValidationData);

                        $app['phraseanet.logger']($BasketElement->getRecord($app)->get_databox())
                            ->log($BasketElement->getRecord($app), \Session_Logger::EVENT_PUSH, $participant_user->get_id(), '');

                        $Participant->addData($ValidationData);
                    }

                    $Participant = $app['EM']->merge($Participant);

                    $app['EM']->flush();

                    $arguments = array('ssel_id' => $Basket->getId());

                    if (!$app['phraseanet.registry']->get('GV_enable_push_authentication') || !$request->get('force_authentication')) {
                        $arguments['LOG'] = $app['tokens']->getUrlToken(
                            \random::TYPE_VALIDATE,
                            $participant_user->get_id(),
                            null,
                            $Basket->getId()
                        );
                    }

                    $url = $app->url('lightbox_validation', $arguments);

                    $receipt = $request->get('recept') ? $app['authentication']->getUser()->get_email() : '';

                    $params = array(
                        'from'       => $app['authentication']->getUser()->get_id(),
                        'from_email' => $app['authentication']->getUser()->get_email(),
                        'to'         => $participant_user->get_id(),
                        'to_email'   => $participant_user->get_email(),
                        'to_name'    => $participant_user->get_display_name(),
                        'url'        => $url,
                        'accuse'     => $receipt,
                        'message'    => $request->request->get('message'),
                        'ssel_id'    => $Basket->getId(),
                        'duration'   => (int) $request->request->get('duration'),
                    );

                    $app['events-manager']->trigger('__PUSH_VALIDATION__', $params);
                }

                $Basket = $app['EM']->merge($Basket);
                $Validation = $app['EM']->merge($Validation);

                $app['EM']->flush();

                $message = sprintf(
                    _('%1$d records have been sent for validation to %2$d users')
                    , count($pusher->get_elements())
                    , count($request->request->get('participants'))
                );

                $ret = array(
                    'success' => true,
                    'message' => $message
                );

                $app['EM']->commit();
            } catch (ControllerException $e) {
                $ret['message'] = $e->getMessage();
                $app['EM']->rollback();
            }

            return $app->json($ret);
        })->bind('prod_push_validate');

        $controllers->get('/user/{usr_id}/', function (Application $app, $usr_id) use ($userFormatter) {
            $datas = null;

            $request = $app['request'];

            $query = new \User_Query($app);

            $query->on_bases_where_i_am($app['authentication']->getUser()->ACL(), array('canpush'));

            $query->in(array($usr_id));

            $result = $query->include_phantoms()
                    ->limit(0, 1)
                    ->execute()->get_results();

            if ($result) {
                foreach ($result as $user) {
                    $datas = $userFormatter($user);
                }
            }

            return $app->json($datas);
        })->assert('usr_id', '\d+');

        $controllers->get('/list/{list_id}/', function (Application $app, $list_id) use ($listFormatter) {
            $datas = null;

            $repository = $app['EM']->getRepository('\Entities\UsrList');

            $list = $repository->findUserListByUserAndId($app, $app['authentication']->getUser(), $list_id);

            if ($list) {
                $datas = $listFormatter($list);
            }

            return $app->json($datas);
        })
            ->bind('prod_push_lists_list')
            ->assert('list_id', '\d+');

        $controllers->post('/add-user/', function (Application $app, Request $request) use ($userFormatter) {
            $result = array('success' => false, 'message' => '', 'user'    => null);

            try {
                if (!$app['authentication']->getUser()->ACL()->has_right('manageusers'))
                    throw new ControllerException(_('You are not allowed to add users'));

                if (!$request->request->get('firstname'))
                    throw new ControllerException(_('First name is required'));

                if (!$request->request->get('lastname'))
                    throw new ControllerException(_('Last name is required'));

                if (!$request->request->get('email'))
                    throw new ControllerException(_('Email is required'));

                if (!\Swift_Validate::email($request->request->get('email')))
                    throw new ControllerException(_('Email is invalid'));
            } catch (ControllerException $e) {
                $result['message'] = $e->getMessage();

                return $app->json($result);
            }

            $user = null;
            $email = $request->request->get('email');

            try {
                $usr_id = \User_Adapter::get_usr_id_from_email($app, $email);
                $user = \User_Adapter::getInstance($usr_id, $app);

                $result['message'] = _('User already exists');
                $result['success'] = true;
                $result['user'] = $userFormatter($user);
            } catch (\Exception $e) {

            }

            if (!$user instanceof \User_Adapter) {
                try {
                    $password = \random::generatePassword();

                    $user = \User_Adapter::create($app, $email, $password, $email, false);

                    $user->set_firstname($request->request->get('firstname'))
                        ->set_lastname($request->request->get('lastname'));

                    if ($request->request->get('company'))
                        $user->set_company($request->request->get('company'));
                    if ($request->request->get('job'))
                        $user->set_company($request->request->get('job'));
                    if ($request->request->get('form_geonameid'))
                        $user->set_geonameid($request->request->get('form_geonameid'));

                    $result['message'] = _('User successfully created');
                    $result['success'] = true;
                    $result['user'] = $userFormatter($user);
                } catch (\Exception $e) {
                    $result['message'] = _('Error while creating user');
                }
            }

            return $app->json($result);
        })->bind('prod_push_do_add_user');

        $controllers->get('/add-user/', function (Application $app, Request $request) {
            $params = array('callback' => $request->query->get('callback'));

            return $app['twig']->render('prod/User/Add.html.twig', $params);
        })->bind('prod_push_add_user');

        $controllers->get('/search-user/', function (Application $app) use ($userFormatter, $listFormatter) {
            $request = $app['request'];

            $query = new \User_Query($app);

            $query->on_bases_where_i_am($app['authentication']->getUser()->ACL(), array('canpush'));

            $query->like(\User_Query::LIKE_FIRSTNAME, $request->query->get('query'))
                ->like(\User_Query::LIKE_LASTNAME, $request->query->get('query'))
                ->like(\User_Query::LIKE_LOGIN, $request->query->get('query'))
                ->like_match(\User_Query::LIKE_MATCH_OR);

            $result = $query->include_phantoms()
                    ->limit(0, 50)
                    ->execute()->get_results();

            $repository = $app['EM']->getRepository('\Entities\UsrList');

            $lists = $repository->findUserListLike($app['authentication']->getUser(), $request->query->get('query'));

            $datas = array();

            if ($lists) {
                foreach ($lists as $list) {
                    $datas[] = $listFormatter($list);
                }
            }

            if ($result) {
                foreach ($result as $user) {
                    $datas[] = $userFormatter($user);
                }
            }

            return $app->json($datas);
        });

        $controllers->match('/edit-list/{list_id}/', function (Application $app, Request $request, $list_id) {

            $repository = $app['EM']->getRepository('\Entities\UsrList');

            $list = $repository->findUserListByUserAndId($app, $app['authentication']->getUser(), $list_id);

            $query = new \User_Query($app);

            $query->on_bases_where_i_am($app['authentication']->getUser()->ACL(), array('canpush'));

            if ($request->get('query')) {
                $query->like($request->get('like_field'), $request->get('query'))
                    ->like_match(\User_Query::LIKE_MATCH_OR);
            }
            if (is_array($request->get('Activity'))) {
                $query->haveActivities($request->get('Activity'));
            }
            if (is_array($request->get('Template'))) {
                $query->haveTemplate($request->get('Template'));
            }
            if (is_array($request->get('Company'))) {
                $query->inCompanies($request->get('Company'));
            }
            if (is_array($request->get('Country'))) {
                $query->inCountries($request->get('Country'));
            }
            if (is_array($request->get('Position'))) {
                $query->havePositions($request->get('Position'));
            }

            $sort = $request->get('srt', 'usr_creationdate');
            $ord = $request->get('ord', 'desc');

            $perPage = 10;
            $offset_start = Max(((int) $request->get('page') - 1) * $perPage, 0);

            $query->sort_by($sort, $ord);

            $results = $query->include_phantoms()
                    ->limit($offset_start, $perPage)
                    ->execute()->get_results();

            $params = array(
                'query'   => $query
                , 'results' => $results
                , 'list'    => $list
                , 'sort'    => $sort
                , 'ord'     => $ord
            );

            if ($request->get('type') === 'fragment') {
                return new Response(
                        $app['twig']->render('prod/actions/Feedback/ResultTable.html.twig', $params)
                );
            } else {
                return new Response(
                        $app['twig']->render('prod/actions/Feedback/list.html.twig', $params)
                );
            }
        })
            ->bind('prod_push_list_edit')
            ->assert('list_id', '\d+');

        return $controllers;
    }
}
