<?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\Model;

use Illuminate\Database\Eloquent\Model;
use Bitverse\Identicon\Identicon;
use Bitverse\Identicon\Color\Color;
use Bitverse\Identicon\Generator\RingsGenerator;
use Bitverse\Identicon\Preprocessor\MD5Preprocessor;
use AuthStack\Driver\Errors\InvalidUserAttributesError;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class User extends Model
{
    protected $table = 'users';
    protected $fillable = ['conn_id', 'unique_id', 'username', 'password', 'config', 'created_at', 'updated_at', 'authenticated_at'];
    public $timestamps = true;

    protected $casts = [
        'config' => 'array',
    ];

    /**
     * @param $value
     */
    public function setConfigAttribute($value)
    {
        $this->attributes['config'] = json_encode($value, JSON_UNESCAPED_SLASHES);
    }

    /**
     * @return bool
     */
    public function updateAuthenticatedAt()
    {
        $this->authenticated_at = $this->freshTimestampString();

        return $this->save();
    }

    /**
     * @param array $config
     *
     * @return $this
     */
    public function updateUserConfig(array $config)
    {
        $cfg = $this->getAttribute('config');

        if (!is_array($cfg)) {
            $cfg = [];
        }

        $cfg = array_merge($cfg, $config);

        $this->attributes['config'] = json_encode($cfg);

        return $this;
    }

    /**
     * @param array $attributes
     *
     * @return User
     *
     * @throws InvalidUserAttributesError
     */
    public static function retrieve(array $attributes)
    {
        static::checkAttributes($attributes);

        $attributes = array_merge($attributes, [
                'unique_id' => sha1(sprintf('%d.%s',
                        $attributes['conn_id'],
                        $attributes['username'])
                , true),

                'config' => [
                    'image' => sprintf("data:image/svg+xml;base64,%s", static::generateRandomImage($attributes['conn_id'] .'+'. $attributes['username']))
                ]
            ]
        );

        try
        {
            $model = static::where('unique_id', $attributes['unique_id'])->firstOrFail();
        }
        catch (ModelNotFoundException $e)
        {
            // No such record, provison the user
            $model = static::create($attributes);
        }
        catch (\PDOException $e)
        {
            $model = static::where('unique_id', $attributes['unique_id'])->firstOrFail();
        }

        return $model;
    }

    /**
     * @param $string
     * @return string
     */
    public static function generateRandomImage($string)
    {
        $generator = new RingsGenerator();
        $generator->setBackgroundColor(Color::parseHex('#EEEEEE'));

        $identicon = new Identicon(new MD5Preprocessor(), $generator);

        $icon = $identicon->getIcon($string);

        return base64_encode($icon);
    }

    /**
     * @return string
     */
    public function getProfileImage()
    {
        return $this->getFromConfig('image', '');
    }


    /**
     * @return string
     */
    public function getTimeZone()
    {
        return $this->getFromConfig('timezone', 'UTC');
    }


    /**
     * @return bool
     */
    public function twoFactorEnabled()
    {
        return $this->getFromConfig('twoFactor.enabled', false);
    }


    /**
     * @return string
     */
    public function getDateTimeFormat()
    {
        return $this->getFromConfig('date_format', 'DD.MM.YYYY');
    }


    /**
     * @param array $attributes
     *
     * @throws InvalidUserAttributesError
     */
    protected static function checkAttributes(array $attributes)
    {
        $required = ['conn_id', 'username'];

        foreach ($required as $key) {
            if (!isset($attributes[$key])) {
                throw new InvalidUserAttributesError(sprintf('Required key %s not specified in the attributes array', $key));
            }
        }
    }

    /**
     * @param $path
     * @param null $default
     * @return array|mixed|null
     */
    public function getFromConfig($path, $default = null)
    {
        $data = isset($this->config) && is_array($this->config) ? $this->config : [];

        // The path with dot notation isn't a string - bye bye
        if(!is_string($path))
        {
            return $default;
        }

        // Do we have a dot?
        if(false !== strpos($path, '.'))
        {
            // Find the segments
            $segments = explode('.', $path);

            // Get the property we're checking
            $result = $data;

            // Start the magic loop
            foreach($segments as $segment)
            {
                if(isset($result[$segment]))
                {
                    // We have the segment, advance the array
                    $result = $result[$segment];
                }
                else
                {
                    // The segment isn't there, return default value
                    return $default;
                }
            }

            return $result;
        }


        // We don't have a dot
        // Check if the $property has
        return isset($data[$path]) ? $data[$path] : $default;
    }


    /**
     * @param string $key
     * @param null   $default
     *
     * @return mixed
     */
    protected function fromConfig($key = '', $default = null)
    {








        if(isset($this->config) && is_array($this->config) && isset($this->config[$key]))
        {
            return $this->config[$key];
        }

        return $default;
    }
}
