<?php

namespace Jakyeru\Larascord\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use App\Providers\RouteServiceProvider;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Jakyeru\Larascord\Http\Requests\StoreUserRequest;
use Jakyeru\Larascord\Services\DiscordService;

use RealRashid\SweetAlert\Facades\Alert;

class DiscordController extends Controller
{
    /**
     * Handles the Discord OAuth2 login.
     */
    public function handle(StoreUserRequest $request): RedirectResponse | JsonResponse
    {
        // Making sure the "guilds" scope was added to .env if there are any guilds specified in "larascord.guilds".
        if (count(config('larascord.guilds'))) {
            if (!in_array('guilds', explode('&', config('larascord.scopes')))) {
                return $this->throwError('missing_guilds_scope');
            }
        }

        // Getting the accessToken from the Discord API.
        try {
            $accessToken = (new DiscordService())->getAccessTokenFromCode($request->get('code'));
        } catch (\Exception $e) {
            return $this->throwError('invalid_code', $e);
        }

        // Get the user from the Discord API.
        try {
            $user = (new DiscordService())->getCurrentUser($accessToken);
            $user->setAccessToken($accessToken);
        } catch (\Exception $e) {
            return $this->throwError('authorization_failed', $e);
        }

        // Making sure the user has an email if the email scope is set.
        if (in_array('email', explode('&', config('larascord.scopes')))) {
            if (empty($user->email)) {
                return $this->throwError('missing_email');
            }
        }

        if (auth()->check()) {
            // Making sure the current logged-in user's ID is matching the ID retrieved from the Discord API.
            if (auth()->id() !== (int)$user->id) {
                auth()->logout();
                return $this->throwError('invalid_user');
            }

            // Confirming the session in case the user was redirected from the password.confirm middleware.
            $request->session()->put('auth.password_confirmed_at', time());
        }

        // Trying to create or update the user in the database.
        // Initiating a database transaction in case something goes wrong.
        DB::beginTransaction();
        try {
            $user = (new DiscordService())->createOrUpdateUser($user);
            $user->accessToken()->updateOrCreate([], $accessToken->toArray());
        } catch (\Exception $e) {
            DB::rollBack();
            return $this->throwError('database_error', $e);
        }

        // Verifying if the user is soft-deleted.
        if (Schema::hasColumn('users', 'deleted_at')) {
            if ($user->trashed()) {
                DB::rollBack();
                return $this->throwError('user_deleted');
            }
        }

        // Patreon check
        try {
            if (!$accessToken->hasScopes(['guilds', 'guilds.members.read'])) {
                DB::rollBack();
                return $this->throwError('missing_guilds_members_read_scope');
            }
            $guildMember = (new DiscordService())->getGuildMember($accessToken, config('discord.guild_id'));
            $patreonroles = config('discord.patreon_roles');
            $user->is_patreon = false;
            if ((new DiscordService())->hasRoleInGuild($guildMember, $patreonroles)) {
                $user->is_patreon = true;
            }
            $user->save();
        } catch (\Exception $e) {
            // Clearly not a patreon
            $user->is_patreon = false;
            $user->save();
        }

        // Committing the database transaction.
        DB::commit();

        // Authenticating the user if the user is not logged in.
        if (!auth()->check()) {
            auth()->login($user, config('larascord.remember_me', false));
        }

        // Redirecting the user to the intended page or to the home page.
        return redirect()->intended(RouteServiceProvider::HOME);
    }

    /**
     * Handles the throwing of an error.
     */
    private function throwError(string $message, \Exception $exception = NULL): RedirectResponse | JsonResponse
    {
        if (app()->hasDebugModeEnabled()) {
            return response()->json([
                'larascord_message' => config('larascord.error_messages.' . $message),
                'message' => $exception?->getMessage(),
                'code' => $exception?->getCode()
            ]);
        } else {
            if (config('larascord.error_messages.' . $message . '.redirect')) {
                Alert::error('Error', config('larascord.error_messages.' . $message . '.message', 'An error occurred while trying to log you in.'));
                return redirect(config('larascord.error_messages.' . $message . '.redirect'))->with('error', config('larascord.error_messages.' . $message . '.message', 'An error occurred while trying to log you in.'));
            } else {
                return redirect('/')->with('error', config('larascord.error_messages.' . $message, 'An error occurred while trying to log you in.'));
            }
        }
    }

    /**
     * Handles the deletion of the user.
     */
    public function destroy(): RedirectResponse | JsonResponse
    {
        // Revoking the OAuth2 access token.
        try {
            (new DiscordService())->revokeAccessToken(auth()->user()->accessToken()->first()->refresh_token);
        } catch (\Exception $e) {
            return $this->throwError('revoke_token_failed', $e);
        }

        // Deleting the user from the database.
        auth()->user()->delete();

        // Showing the success message.
        if (config('larascord.success_messages.user_deleted.redirect')) {
            return redirect(config('larascord.success_messages.user_deleted.redirect'))->with('success', config('larascord.success_messages.user_deleted.message', 'Your account has been deleted.'));
        } else {
            return redirect('/')->with('success', config('larascord.success_messages.user_deleted', 'Your account has been deleted.'));
        }
    }
}
