<?php

namespace TdyAdmins\Controller;

use Application\Session\Container;
use TdyAdmins\Authentication\Adapter\DbTable\BcryptTreatment;
use TdyAdmins\InputFilter\Login as LoginFilter;
use TdyAdmins\Model\Admin;
use TdyCommons\Controller\BaseWebController;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Storage;
use Zend\Authentication\Result as AuthenticationResult;
use Zend\Http\PhpEnvironment\Request as PhpRequest;
use Zend\Http\PhpEnvironment\Response as PhpResponse;
use Zend\Session;
use Zend\View\Model\JsonModel;
use Zend\View\Model\ViewModel;


class LoggingController extends BaseWebController
{

    /**
     * @var AuthenticationService
     */
    protected $authServiceDb;

    /**
     * @var Storage\Session
     */
    protected $authStorage;

    /**
     * @return AuthenticationService
     */
    public function getAuthServiceDb()
    {
        if (!$this->authServiceDb) {
            $this->authServiceDb = $this->getServiceLocator()->get('AuthService\Db');
        }

        return $this->authServiceDb;
    }

    /**
     * @return JsonModel|ViewModel
     */
    public function loginAction()
    {
        $errors = [];

        /** @var PhpRequest $phpRequest */
        $phpRequest = $this->getRequest();

        /** @var PhpResponse $phpResponse */
        $phpResponse = $this->getResponse();

        $filter = new LoginFilter();
        $isXhr  = $phpRequest->isXmlHttpRequest();

        if ($phpRequest->isGet()) {
            $this->layout('layout/login');

            return new ViewModel();

        } else {
            if ($phpRequest->isPost()) {

                if ($isXhr) {
                    $data = [];
                    $body = $phpRequest->getContent();
                    if (!empty($body)) {
                        $data = json_decode($body, true);
                    }
                } else {
                    $data = $this->params()->fromPost();
                }

                $filter->setData($data);

                if ($filter->isValid()) {
                    $result = $this->authenticate($data);

                    switch ($result->getCode()) {
                        case AuthenticationResult::SUCCESS:
                            break;
                        case AuthenticationResult::FAILURE_IDENTITY_NOT_FOUND:
                        case AuthenticationResult::FAILURE_CREDENTIAL_INVALID:
                        case AuthenticationResult::FAILURE_IDENTITY_AMBIGUOUS:
                            if ($isXhr) {
                                $errors[] = [
                                    'code' => 'ADM-LOG-001',
                                    'name' => 'Invalid username or password.'
                                ];
                            } else {
                                $this->flashMessenger()->setNamespace('login')->addErrorMessage('Invalid username or password.');
                            }
                            break;
                        default:
                            if ($isXhr) {
                                $errors[] = [
                                    'code' => 'ADM-LOG-002',
                                    'name' => 'Unknown login error.'
                                ];
                            } else {
                                $this->flashMessenger()->setNamespace('login')->addErrorMessage('Unknown login error.');
                            }
                            break;
                    }

                    if ($result->isValid()) {
                        /** @var BcryptTreatment $adapter */
                        $adapter = $this->getAuthServiceDb()->getAdapter();
                        $data    = $adapter->getResultRowObject(null, ['password']);
                        $user    = new Admin(get_object_vars($data));
                        $this->getAuthServiceDb()->getStorage()->write($user->id);

                        $session = new Container(SESSION_CONTAINER);
                        if (!isset($session->user)) {
                            $session->user = $user;
                        }

                        if ($isXhr) {
                            $phpResponse->getHeaders()->addHeaderLine('Content-type', 'application/json');
                            $jsonView = new JsonModel();
                            $jsonView->setTerminal(true);
                            $jsonView->setVariables([
                                'response' => [
                                    'data'   => [
                                        'response' => true,
                                    ],
                                    'errors' => []
                                ],
                            ]);

                            return $jsonView;
                        } else {
                            return $this->redirect()->toRoute('home');
                        }
                    } else {
                        if ($isXhr) {
                            $phpResponse->getHeaders()->addHeaderLine('Content-type', 'application/json');
                            $jsonView = new JsonModel();
                            $jsonView->setTerminal(true);
                            $jsonView->setVariables([
                                'response' => [
                                    'data'   => [
                                        'response' => false,
                                    ],
                                    'errors' => $errors
                                ],
                            ]);

                            return $jsonView;
                        } else {
                            return $this->redirect()->toRoute('login');
                        }
                    }
                } else {
                    $messages = $filter->getMessages();
                    foreach ($messages as $message) {
                        foreach ($message as $code => $description) {
                            if ($isXhr) {
                                $errors[] = [
                                    'code' => $code,
                                    'name' => $description
                                ];
                            } else {
                                $this->flashMessenger()->setNamespace('error')->addMessage($description);
                            }
                        }
                    }

                    if ($isXhr) {
                        $phpResponse->getHeaders()->addHeaderLine('Content-type', 'application/json');
                        $jsonView = new JsonModel();
                        $jsonView->setTerminal(true);
                        $jsonView->setVariables([
                            'response' => [
                                'data'   => [
                                    'response' => false,
                                ],
                                'errors' => $errors
                            ],
                        ]);

                        return $jsonView;
                    } else {
                        return $this->redirect()->toRoute('login');
                    }

                }
            } else {
                return new ViewModel();
            }
        }

    }

    /**
     * @return \Zend\Http\Response
     */
    public function logoutAction()
    {
        $this->getAuthServiceDb()->clearIdentity();
        $session = new Container(SESSION_CONTAINER);
        $session->getManager()->destroy();

        return $this->redirect()->toRoute('login');
    }

    /**
     * @return ViewModel
     */
    public function scriptAction()
    {
        /** @var PhpResponse $phpResponse */
        $phpResponse = $this->getResponse();
        $phpResponse->getHeaders()->addHeaderLine('Content-type', 'text/javascript');
        $view = new ViewModel();
        $view->setTerminal(true);
        $view->setTemplate('tdy-admins/logging/script.js');

        return $view;
    }

    /**
     * @param array $request
     *
     * @return AuthenticationResult
     */
    protected function authenticate($request)
    {
        $serviceDb = $this->getAuthServiceDb();

        $identity   = $request['username'];
        $credential = $request['password'];

        /** @var BcryptTreatment $adapter */
        $adapter = $serviceDb->getAdapter();
        $adapter->setIdentity($identity)->setCredential($credential);

        $result = $serviceDb->authenticate();

        return $result;
    }

}
