<?php

/*
 * This file is part of the AuthStack project.
 *
 * (c) Buckhill Ltd <development@buckhill.co.uk>
 *
 * This source file is subject to the Buckhill license that is bundled
 * with this source code in the file LICENSE.
 */

namespace AuthStack\Driver\Collection;

use AuthStack\Driver\Model\U2F\AuthChallenge;
use Buckhill\U2F\U2FKey;
use function \Buckhill\U2F\u2f;
use AuthStack\TwoFactor\Model\Key;
use AuthStack\Driver\Model\SecurityKey;
use AuthStack\TwoFactor\Model\KeyInterface;
use AuthStack\Driver\Errors\NonexistentKeyError;
use AuthStack\TwoFactor\Collection\KeyCollectionInterface;

class TwoFactorKeys implements KeyCollectionInterface
{
    protected $keys = [];
    protected $index = [];

    protected $count_otp = 0;
    protected $count_u2f = 0;

    /**
     * @param array $keys
     *
     * @return TwoFactorKeys|KeyCollectionInterface
     */
    public static function fromArray(array $keys)
    {
        $me = new static();

        foreach ($keys as $key) {
            $me->add(Key::fromArray($key));
        }

        return $me;
    }

    /**
     * @param KeyInterface $key
     *
     * @return $this
     */
    public function add(KeyInterface $key)
    {
        $this->keys[] = $key;

        $this->index[$key->getID()] = current($this->keys);

        if ($key->isU2F()) {
            ++$this->count_u2f;
        } else {
            ++$this->count_otp;
        }

        return $this;
    }

    /**
     * @param $id
     *
     * @return bool
     */
    public function has($id)
    {
        return isset($this->index[$id]);
    }

    /**
     * @param $id
     *
     * @return mixed
     *
     * @throws \Exception
     */
    public function getById($id)
    {
        if (!$this->has($id)) {
            throw new NonexistentKeyError('No such key associated with user: '.$id);
        }

        return $this->index[$id];
    }

    /**
     * @return array
     */
    public function getU2FKeyIds()
    {
        $result = [];

        foreach ($this->keys as $key) {
            /** @var $key KeyInterface */
            if ($key->isU2F()) {
                $result[] = $key->getID();
            }
        }

        return $result;
    }

    /**
     * @return array
     */
    public function getOTPKeyIds()
    {
        $result = [];

        foreach ($this->keys as $key) {
            /** @var $key KeyInterface */
            if (!$key->isU2F()) {
                $result[] = $key->getID();
            }
        }

        return $result;
    }

    /**
     * @return int
     */
    public function size()
    {
        return sizeof($this->keys);
    }

    /**
     * valid values are 'otp' and 'u2f'.
     *
     * @param $what
     *
     * @return int
     */
    public function count($what)
    {
        $result = 0;

        switch ($what) {
            case self::TYPE_U2F:
                $result = $this->count_u2f;
                break;

            case self::TYPE_OTP:
                $result = $this->count_otp;
                break;
        }

        return $result;
    }

    /**
     * @return bool
     */
    public function hasU2F()
    {
        return $this->count_u2f > 0;
    }

    /**
     * Loads U2F keys, creates a challenge, saves it to database, returns an array of challenges
     * If there is no U2F key present, it returns an empty array
     *
     * @return array
     */
    public function U2FAuthChallenge()
    {
        $keys = $this->filter(self::TYPE_U2F);

        $result = [];

        foreach($keys as $key)
        {
            /** @var $key KeyInterface */

            // Creates the database record (model)
            $model = AuthChallenge::fromKeyId($key->getID());

            // Fetches the challenge which we need to deliver to JS app
            $result[] = $model->getChallenge();
        }

        return $result;
    }

    public function U2FVerify()
    {}

    /**
     * @param $type
     *
     * @return array
     */
    public function filter($type)
    {
        $cond = $type === self::TYPE_U2F ? true : false;

        $result = [];

        foreach ($this->keys as $key) {
            /*
             * @var KeyInterface
             */
            if ($key->isU2F() === $cond) {
                $result[] = $key;
            }
        }

        return $result;
    }

    /**
     * @return array
     */
    public function all()
    {
        return $this->keys;
    }

    /**
     * @return \Illuminate\Support\Collection
     */
    public function toArray()
    {
        if (!sizeof($this->keys)) {
            return collect([]);
        }

        return collect($this->keys)->map(function ($key) {
            /* @var $key KeyInterface */
            return $key->toArray();
        });
    }
}
