<?php

namespace Auth;

use Fuel\Core\Log;
use Fuel\Core\Profiler;
use Fuel\Core\Session;

/**
 * SETI Consultyn S.L.
 * www.seticonsultyn.es
 *
 * Copyright(c) 2011-2013
 *
 * PHP Version 5.4.x
 *
 * @category  Libraries
 * @package   App
 * @author    Ignacio Muñoz @ SETI Consultyn S.L. <contacto@seticonsultyn.es>
 * @copyright 2011-2013 SETI Consultyn S.L. http://seticonsultyn.com/copyright.txt
 * @license   SETI Consultyn Software License <license.txt>
 * @version   GIT: $Rev$
 * @link      http://seticonsultyn.com
 */
class Auth
{
    protected static $_usernames = array(
        'username',
        'email'
    );

    protected static $token;

    protected static $user;

    protected static $roles;

    protected static $groups;

    /**
     * _init
     */
    public static function _init()
    {
        static::$token = Session::get(static::token());
    }

    /**
     * Returns the model of the users
     *
     * @return \Orm\Query
     */
    public static function model()
    {
        return Model_Auth_User::query();
    }

    /**
     * Attempts to login a user, if the user is valid return true, not false.
     *
     * @param array $arguments
     * @return bool
     * @throws UserDisabledException
     * @throws UserPasswordIncorrectException
     * @throws UserUnverifiedException
     * @throws UserNotFoundException
     */
    public static function attempt($arguments = array())
    {
        $valid = false;

        $usernames = static::$_usernames;
        $usernames = (!is_array($usernames))
            ? array($usernames)
            : $usernames;

        foreach($usernames as $identify_by)
        {
            $user = static::model()
                ->where($identify_by, '=', $arguments['username'])
                ->get_one();

            if(!is_null($user))
            {
Log::error("VALIDADO");
                if(!static::check_hash($arguments['password'], $user->password))
                {
                    throw new UserPasswordIncorrectException("User password is incorrect");
                }

                if($user->is_confirmed !== "S")
                {
                    throw new UserUnverifiedException("User is unconfirmed");
                }

                if($user->is_active !== "S")
                {
                    throw new UserDisabledException("User is disabled");
                }

                $valid = true;
                break;
            }
        }

        if($valid)
        {
            return static::login($user->id);
        }
        else
        {
            throw new UserNotFoundException("User not found");
        }
    }

    /**
     * Check if the current user is a guest
     *
     * @return bool
     */
    public static function guest()
    {
        return !static::check();
    }

    /**
     * Check if the current user is logged
     *
     * @return bool
     */
    public static function check()
    {
        return static::user() !== false;
    }

    /**
     * Check if the user is from a grupo
     *
     * @param $grupo string
     */
    public static function is($grupo)
    {
        return static::user()->group->nombre==$grupo?true:false;
      //return array_key_exists($grupo, static::groups());
    }


    /**
     * Check if the user is from a grupo (By Id)
     *
     * @param $grupo id
     */
    public static function is_id($id_grupo)
    {
        return static::user()->group->id==$id_grupo?true:false;
        //return array_key_exists($grupo, static::groups());
    }

    /**
     * Makes the login of the user into app
     *
     * @param $token
     * @return bool
     */
    public static function login($token)
    {
        static::$token = $token;
        static::store($token);

        return true;
    }

    /**
     * Destroy the user and logout it
     */
    public static function logout()
    {
        static::$user = null;
        Session::delete(static::token());
        Session::destroy();
        static::$token = null;
    }


    /**
     * Hash a password giving a string and salt
     *
     * @param $password string
     * @param $user_salt string
     * @return string
     */
    public static function hash($password)
    {
        $work = str_pad(8, 2, '0', STR_PAD_LEFT);

        // Bcrypt expects the salt to be 22 base64 encoded characters including
        // dots and slashes. We will get rid of the plus signs included in the
        // base64 data and replace them with dots.
        if (function_exists('openssl_random_pseudo_bytes'))
        {
            $salt = openssl_random_pseudo_bytes(16);
        }
        else
        {
            $salt = Str::random(40);
        }

        $salt = substr(strtr(base64_encode($salt), '+', '.'), 0 , 22);

        return crypt($password, '$2a$'.$work.'$'.$salt);
    }

    /**
     * Check if a valid combination of password, salt and hash
     *
     * @param $password string
     * @param $salt string
     * @param $hash string
     * @return bool
     */
    public static function check_hash($password,  $hash)
    {
        return crypt($password, $hash) === $hash;
    }

    public static function groups()
    {
        if(!is_null(static::$groups)) return static::$groups;
        if(static::user() === false)
        {
            $g = Model_Auth_Group::query()
                ->related('roles')
                ->where('id', 1) // THIS IS ANONYMOUS DO NO DELETE
                ->get_one();

            if($g === null)
            {
                throw new \Exception("Group Anonymous id: 1 is deleted the auth cant work like this.");
            }

            static::$groups[$g->name] = new \stdClass;
            static::$groups[$g->name]->name = $g->name;
            static::$groups[$g->name]->created_at = $g->created_at;
            static::$groups[$g->name]->updated_at = $g->updated_at;
            static::$groups[$g->name]->roles = $g->roles;
        }
        else
        {
            foreach(static::user()->groups as $g)
            {
                static::$groups[$g->name] = new \stdClass;
                static::$groups[$g->name]->name = $g->name;
                static::$groups[$g->name]->created_at = $g->created_at;
                static::$groups[$g->name]->updated_at = $g->updated_at;
            }
        }



        return static::$groups;
    }

    /**
     * get the roles compossed
     *
     * @return array
     */
    public static function roles()
    {
        if(!is_null(static::$roles)) return static::$roles;

        static::$roles = array();

        if(static::user() === false)
        {
            foreach(static::groups() as $g)
            {
                foreach($g->roles as $r)
                {
                    static::$roles[] = $r->permission;
                }
            }
        }
        else
        {
            foreach(static::user()->group->roles as $g)
            {
                    static::$roles[] = $g->role->permission;
            }

            /*
            foreach(static::user()->groups as $g)
            {
                foreach($g->roles as $r)
                {
                    static::$roles[] = $r->permission;
                }
            }
            */
        }

        return static::$roles;
    }

    /**
     * check if the user can perfom a role
     *
     * @param $rol
     * @return bool
     */
    public static function can($roles)
    {
        if(!is_array($roles))
        {
            $roles = array($roles);
        }

        foreach($roles as $rol)
        {
            if(in_array($rol, static::roles()) or in_array('superadmin', static::roles()))
            {
                return true;
            }
        }

        return false;
    }

    /**
     * Gets the auth token
     *
     * @return string
     */
    protected static function token()
    {
        return 'auth_login';
    }

    /**
     * retrieves the user
     *
     * @return \Orm\Model
     */
    public static function user()
    {
        if(!is_null(static::$user)){
            return static::$user;
        }
        return static::$user = static::retrieve((int)static::$token);
    }

    /**
     * @return int
     *
     *  Devuelve el id del usuario actual;
     *
     */
    public static function get_user_id()
    {
        return static::$token;
    }

    /**
     * Retrieves the user by id
     *
     * @param $id
     * @return \Orm\Model
     */
    public static function retrieve($id)
    {
        if(filter_var($id, FILTER_VALIDATE_INT) !== false) {
            $user = static::model()
                ->related('group')
                //->related('group.roles')
                ->where('id', $id)
                ->get_one();
/*
            $user = static::model()
                ->related('groups')
                ->related('groups.roles')
                ->related('clients')
                ->where('id', $id)
                ->get_one();
*/
            if($user !== null)
            {
                return $user;
            }

            return false;
        }

        throw new \Exception("Auth::retrieve specs a integer as a parameter");
    }

    /**
     *
     *
     * @param $token
     */
    protected static function store($token)
    {
        Session::set(static::token(), $token);
    }

}

class UserNotFoundException extends \Exception {};
class UserUnverifiedException extends \Exception {};
class UserDisabledException extends \Exception {};
class UserDeletedException extends \Exception {};
class UserPasswordIncorrectException extends \Exception {};