<?php

namespace App\Libs\Services;

use App\Exceptions\NotFoundException;
use App\Exceptions\ServiceException;
use App\Models\User;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Str;

class UserService
{

    /**
     * Creates user and send the email validation email
     *
     * @param  string  $name
     * @param  string  $email
     * @param  string  $password
     * @return User
     */
    public function create(string $name, string $email, string $password): User
    {
        $user = User::where('email', strtolower($email))->where('active', false)->first();

        if (is_null($user)) {
            $user = new User();
        }

        $user->email = $email;
        $user->password = $password;
        $user->name = $name;
        $user->email_verification_code = Str::random(32);
        $user->email_verification_expiration = now()->addMinutes(config('auth.frontend_email_activation_expire'));
        $user->active = false;
        $user->save();
        $user->sendEmailVerificationNotification();

        return $user;
    }

    /**
     * Validates user email
     *
     * @param  string  $code
     * @return User
     * @throws NotFoundException
     * @throws ServiceException
     */
    public function validateEmail(string $code): User
    {
        $user = User::where('email_verification_code', $code)->first();

        if (is_null($user)) {
            throw new NotFoundException(trans('response.api.invalid_email_code'));
        }

        if (is_null($user->email_verification_expiration) || $user->email_verification_expiration->lessThan(now())) {
            throw new ServiceException(trans('response.api.expired_email_code'));
        }

        $user->email_verified_at = now();
        $user->email_verification_code = null;
        $user->email_verification_expiration = null;
        $user->active = true;
        $user->save();
        return $user;
    }

    /**
     * Login a user. Returns the JWT token
     *
     * @param $email
     * @param $password
     * @return string
     * @throws AuthenticationException
     */
    public function loginByUser(User $user): string
    {
        return auth('api')->login($user);
    }

    /**
     * Login a user by email and password. Returns the JWT token
     *
     * @param $email
     * @param $password
     * @return string
     * @throws AuthenticationException
     */
    public function loginByCredentials($email, $password): string
    {
        $credentials = [
            'email'    => strtolower($email),
            'password' => $password
        ];

        if (!$token = auth('api')->attempt($credentials)) {
            throw new AuthenticationException(trans('auth.failed'));
        }

        $loggedUser = $this->getLoggedUser();

        if (!$loggedUser->active) {
            throw new AuthenticationException(trans('auth.user_not_active'));
        }

        if (is_null($loggedUser->email_verified_at)) {
            throw new AuthenticationException(trans('auth.user_not_validated'));
        }

        return $token;
    }

    /**
     * Gets the logged user
     *
     * @return User|null
     */
    public function getLoggedUser(): ?User
    {
        return auth('api')->user();
    }

    /**
     * Get the user list for the backoffice grid
     *
     * @param $page
     * @param $rowCount
     * @param $sort
     * @param $search
     *
     * @return array
     */
    public function getUserGridList($page, $rowCount, $sort, $search)
    {
        Paginator::currentPageResolver( function () use ($page) {
            return $page;
        });

        if (!empty($search)) {
            $query = User::where(function ($query) use ($search) {
                $query->orWhere('name', 'like', "%{$search}%")
                    ->orWhere('email', 'like', "%{$search}%");
            });
        } else {
            $query = User::query();
        }
        if (empty($sort)) {
            $rows = $query->paginate($rowCount);
        } else {
            foreach ($sort as $field => $direction) {
                if(empty($direction) || !Str::is(strtolower($direction),"desc")){
                    $orderDirection = "asc";
                } else {
                    $orderDirection = $direction;
                }
                $rows = $query->orderBy($field, strtolower($orderDirection))->paginate($rowCount);
                break;
            }
        }

        return [
            "total"             => $rows->total(),
            "current"           => $rows->currentPage(),
            "rowCount"          => $rowCount,
            "rows"              => $rows->getCollection()
        ];
    }
}
