<?php

namespace App\Http\Controllers;

use App\User;
use App\UserAccessToken;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use romanzipp\Twitch\Facades\Twitch;
use romanzipp\Twitch\Enums\Scope;

class UserController extends Controller
{
    const OAUTH_BASE_URI = "https://id.twitch.tv/oauth2/";
    const AUTH_REDIRECT_ROUTE = "me.dashboard";

    public function __construct() {
        $this->middleware("guest")->only(["login", "authorized"]);

        $this->middleware("auth")->except(["login", "authorized"]);
    }

    /**
     * Redirect to the Twitch OAuth app authorization site.
     */
    public function login() {
        $stateToken = getToken(25);
        session()->flash("stateToken", $stateToken);

        return redirect()->away(Twitch::getOAuthAuthorizeUrl("code", [
            Scope::USER_READ_EMAIL
        ], $stateToken));
    }

    /**
     * Callback of the Twitch OAuth authorization page.
     *
     * @param Illuminate\Http\Request $request
     */
    public function authorized(Request $request) {
        if ($request->error) {
            session()->flash("error", [
                "title" => "Authorization error",
                "description" => $request->error_description . "."
            ]);

            return redirect(route("home"));

        } else if (session("stateToken") && session("stateToken") != $request->state) {
            session()->flash("error", [
                "title" => "Missmatch state token",
                "description" => "State tokens do not match, maybe someone tried to intercept the login attempt. Try again in sone seconds, if it persists contact with the site administrator."
            ]);
            return redirect(route("home"));
        }

        $oauthTokenRequest = Twitch::getOAuthToken($request->code);

        if (!$oauthTokenRequest->success) {
            return response("Error " . $oauthTokenRequest->data->status . ", " . $oauthTokenRequest->data->message);
        }

        $userAccessToken = $oauthTokenRequest->data->access_token;
        $user = $this->getAuthedUser($userAccessToken);

        $dbUser = User::where("twitch_uid", $user->id)->first();

        if (!$dbUser) {
            $dbUser = $this->store($user);
        }

        $dbUser->accessTokens()->create([
            "access_token" => $userAccessToken,
            "refresh_token" => $oauthTokenRequest->data->refresh_token,
            "expires_in" => $oauthTokenRequest->data->expires_in
        ]);

        Auth::login($dbUser);
        session(["oauthToken" => $userAccessToken]);

        return redirect(route(self::AUTH_REDIRECT_ROUTE));
    }

    /**
     * Get data of a user given a user access token
     *
     * @param string $userAccessToken A valid user access token obtained from a OAuth token request.
     */
    private function getAuthedUser($userAccessToken) {
        $userRequest = Twitch::withToken($userAccessToken)->getAuthedUser();

        if (!$userRequest->success) {
            session()->flash("error", [
                "title" => "Could not get user data",
                "description" => "Something failed while trying to get the user data, is Twitch API down?"
            ]);

            return redirect(route("home"));
        }

        return $userRequest->data[0];
    }

    /**
     * Creates a user record on the database given his data
     *
     * @param Array $data Array that contains the data of the user (id, display_name, email and profile_image_url).
     */
    private function store($data) {
        $user = new User;

        $user->twitch_uid = $data->id;
        $user->username = $data->display_name;
        $user->email = $data->email;
        $user->profile_image = $data->profile_image_url;

        $user->save();

        return $user;
    }

    /**
     * Logout the user from the session and destroy the user access token created for the session.
     *
     * @param Illuminate\Http\Request $request
     */
    public function logout(Request $request) {
        $logoutRequest = $this->invalidateUserOauthToken(session("oauthToken"));

        if (!$logoutRequest->success) {
            session()->flash("error", [
                "title" => "Session could not be ended",
                "description" => "The user oauth token could not be invalidated, is Twitch API down?"
            ]);
            return redirect(route("home"));
        }

        Auth::user()->accessTokens()->where("access_token", session("oauthToken"))->first()->delete();
        Auth::logout();

        session()->forget("oauthToken");

        return redirect(route("home"));
    }

    /**
     * Invalidates the given user oauth token.
     *
     * @param string $oauthToken user oauth token to be invalidated.
     */
    private function invalidateUserOauthToken($oauthToken) {
        return Twitch::post(self::OAUTH_BASE_URI . "revoke", [
            "client_id" => Twitch::getClientId(),
            "token" => $oauthToken
        ]);
    }

    /**
     * Display the user dashboard.
     */
    public function showDashboard() {
        return view("me.dashboard", [
            "followHook" => Auth::user()->twitchWebhooks()->where("type", "follow")->first(),
            "streamHook" => Auth::user()->twitchWebhooks()->where("type", "stream")->first(),
            "subscriptionHook" => Auth::user()->twitchWebhooks()->where("type", "subscription")->first(),
        ]);
    }
}
