<?php

namespace Ignite\Core\Http\Controllers\Auth;

use Exception;
use Ignite\Core\Entities\User;
use Ignite\Core\Traits\StopImpersonating;
use Ignite\Flash\Facades\Flash;
use Ignite\Core\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
use Laravel\Socialite\Facades\Socialite;

class LoginController extends Controller
{
    use AuthenticatesUsers;
    use StopImpersonating;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest', ['except' => 'logout']);
    }

    /**
     * Overrides the default get login username to be used by the controller.
     *
     * @return string
     */
    public function username()
    {
        return 'username';
    }

    /**
     * Override Laravel's default login form.
     *
     * @return View
     */
    public function showLoginForm()
    {
        return view('auth.login');
    }

    /**
     * Attempt to log the user into the application if the user is active.
     *
     * @param Request $request
     *
     * @return bool
     */
    protected function attemptLogin(Request $request)
    {
        $credentials = $this->credentials($request);

        $useEmail = config('core.use_email_as_username', true);
        if ($useEmail) {
            $credentials['email'] = $credentials['username'];
            unset($credentials['username']);
        }

        return $this->guard()->attempt(
            array_merge(['status' => User::STAT_ACTIVE], $credentials),
            $request->has('remember')
        );
    }

    /**
     * Get the failed login response instance.
     *
     * @param Request $request
     *
     * @throws ValidationException
     */
    protected function sendFailedLoginResponse(Request $request)
    {
        $user = User::where('email', $request->only($this->username()))->first();

        if ($user && $user->isInactive()) {
            throw ValidationException::withMessages(['status' => 'Your account is inactive.']);
        }

        if ($user && $user->isPending()) {
            throw ValidationException::withMessages([
                'status' => 'Your account is pending. Please set your password. '
                          . 'If you have not received an email with instructions on setting your password. '
                          . 'Please reset your password.'
            ]);
        }

        throw ValidationException::withMessages([
            $this->username() => [trans('auth.failed')],
        ]);
    }

    /**
     * This will be used instead of the AuthenticateUsers::logout() trait
     * see: use AuthenticatesUsers above
     * Log the user out of the application.
     *
     * @param Request $request
     *
     * @return RedirectResponse
     */
    public function logout(Request $request)
    {
        $impersonating = $this->stopImpersonating();
        if ($impersonating instanceof RedirectResponse) {
            return $impersonating;
        }

        $this->guard()->logout();

        $request->session()->invalidate();
        $request->session()->regenerateToken();

        $this->flash('success', 'You have signed-out.');

        return redirect('/');
    }

    /**
     * Redirect the user to the GitHub authentication page.
     *
     * @param  string $driver
     * @return RedirectResponse
     * @throws Exception
     */
    public function redirectToProvider($driver)
    {
        $driver = htmlspecialchars($driver);

        $drivers = array_filter(config('core.auth.drivers', []));

        if (! in_array($driver, $drivers)) {
            Flash::error("Unknown provider `{$driver}`.");
            return redirect('login');
        }

        try {
            return Socialite::with($driver)->redirect();
        } catch (Exception $e) {
            $this->logException($e);
            Flash::error("Unable to contact provider: `{$driver}`.");
            return redirect('login');
        }
    }

    /**
     * Obtain the user information from GitHub.
     *
     * @param  string $driver
     *
     * @return RedirectResponse
     * @throws Exception
     */
    public function handleProviderCallback($driver)
    {
        $driver = htmlspecialchars($driver);

        $drivers = array_filter(config('core.auth.drivers', []));

        if (! in_array($driver, $drivers)) {
            Flash::error("Unknown provider `{$driver}`.");
            return redirect('login');
        }

        try {
            $user = Socialite::driver($driver)->user();
            if (! empty($user->email)) {
                $user = User::where('email', $user->email)->firstOrFail();
                $this->guard()->login($user);
                return redirect($this->redirectPath());
            }
        } catch (Exception $e) {
            $this->logException($e);
            Flash::error("Unable to authenticate via $driver using e-mail address.");
        }

        return redirect('login');
    }

    /**
     * Get the post register / login redirect path.
     *
     * @return string
     */
    public function redirectPath()
    {
        $intended = Session::get('url.intended', '/');

        if (Str::contains($intended, 'admin')) {
            return redirect($intended);
        }

        if (auth()->user()->can('core.dashboard.browse')) {
            return route('admin.dashboard.index');
        }

        if (method_exists($this, 'redirectTo')) {
            return $this->redirectTo();
        }

        return property_exists($this, 'redirectTo') ? $this->redirectTo : '/';
    }
}
