<?php

namespace App\Http\Controllers;

use App\TwitchWebhook;
use App\WebhookAction;
use App\Http\Requests\AddWebhookAction;
use App\Http\Requests\SubscribeWebhook;
use App\Http\Requests\TestWebhookAction;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use romanzipp\Twitch\Facades\Twitch;
use Woeler\DiscordPhp\Webhook\DiscordWebhook;
use Woeler\DiscordPhp\Message\DiscordEmbedMessage;
use Woeler\DiscordPhp\Message\DiscordTextMessage;

use Illuminate\Support\Facades\Storage;

// TODO: document all the methods/constants
class WebhookController extends Controller
{
    // 10 days
    const MAX_LEASE_SECONDS = 864000;
    const TOPIC_STREAM_CHANGED = "https://api.twitch.tv/helix/streams";

    public function __construct() {
        $this->middleware("auth")->only([
            "subscribe",
            "addAction"
        ]);
    }

    /**
     * Subscribe or unsubscribe from a hook event
     */
    public function subscribe(SubscribeWebhook $request) {
        if ($request->mode === "subscribe") {

            switch($request->type) {
                case "stream":
                    return $this->subscribeStreamHook($request);
                    break;
            }

        } else if ($request->mode === "unsubscribe") {

            $hook = Auth::user()->twitchWebhooks()->where("type", $request->type)->first();

            if ($hook) {
                $hook->disabled_at = Carbon::now();
                $hook->save();
            }

            return response("Ok");
        }
    }

    // TODO: implement in a future more than one action per hook
    public function addAction(AddWebhookAction $request) {
        $hook = Auth::user()->twitchWebhooks()->where("type", $request->type)->firstOrFail();

        // TODO: currently hardcoded, allow only one action
        $actions = $hook->webhookActions();

        // add
        if (count($actions->get()) == 0) {
            $action = $actions->create([
                "discord_hook_url" => $request->discord_hook_url
            ]);

        // edit the existing one
        } else {
            $action = $actions->first();
            $action->discord_hook_url = $request->discord_hook_url;
            $action->save();
        }

        return response()->json(["action_id" => $action->id]);
    }

    /**
     * Sends a message to a given discord webhook url
     */
    public function testWebhook(TestWebhookAction $request) {
        $message = new DiscordTextMessage();
        $message->setContent("Test message.")
            ->setUsername(config("app.name"))
            ->setAvatar(config("app.url") . "/img/twitch-icon.png");

        $webhook = new DiscordWebhook($request->discord_hook_url);
        $webhook->send($message);

        return response()->json($webhook);
    }

    /**
     * Create a webhook to listen for stream changes
     */
    private function subscribeStreamHook(Request $request) {
        $hookCallback = route("hooks.twitch.stream");

        $lease = self::MAX_LEASE_SECONDS;

        if (config("app.env") !== "production") {
            $lease = 3600;
        }

        $topic = self::TOPIC_STREAM_CHANGED . "?user_id=" . Auth::user()->twitch_uid;

        // TODO: implement secrets, global or specified for each hoock?
        // $secret = "create a random secret";

        $twitchWebhook = Auth::user()->twitchWebhooks()->firstOrNew([
            "type" => $request->type
        ], [
            "topic" => $topic,
            "callback" => $hookCallback,
            "expires_at" => Carbon::now()->addSeconds($lease)
        ]);

        $twitchWebhook->disabled = false;
        $twitchWebhook->disabled_at = null;

        if (!$twitchWebhook->id) {
            $twitchWebhook->save();
            $twitchWebhook->callback .= "?hook_id=" . $twitchWebhook->id;
            $subscription = Twitch::subscribeWebhook($twitchWebhook->callback, $topic, $lease);
        }

        $twitchWebhook->save();

        if ((isset($subscription) && $subscription->success) || true) {
            return response("Ok");
        } else {
            return response()->json($subscription, $subscription->status);
        }
    }

    /**
     * Verifies the webhook (reply to Twitch GET callback after hook creation)
     */
    public function verifySubscription(Request $request) {
        $twitchHook = TwitchWebhook::findOrFail($request->hook_id);
        $twitchHook->active = true;
        $twitchHook->save();

        return response($request->input("hub_challenge"), 200)->header("Content-Type", "text/plain");
    }

    // TODO: implement the method
    // listen to follow events
    public function follow(Request $request) {
        if (!$this->verifySecret()) {
            abort(403);
        }

        return "follow";
    }

    /**
     * Stream change hook gets here, executes the user configured hook actions
     */
    // TODO: test this, separate the 3 cases: start, update, end
    public function streamUpdate(Request $request) {
        $twitchWebhook = TwitchWebhook::findOrFail($request->hook_id);

        if (!$this->verifySecret()) {
            abort(403);

        } else if ($twitchWebhook->disabled) {
            return response("Webhook disabled", 200);
        }

        $eventType = null;
        $data = $request->all()["data"];
        Storage::put("stream_hook.json", json_encode($data));

        if ($data) {

            $data = $data[0];

            if (empty($twitchWebhook->live_since)) {
                $eventType = "start";
                $twitchWebhook->live_since = Carbon::now();
                $twitchWebhook->offline_since = null;
                $twitchWebhook->save();

            } else {
                $eventType = "update";
            }

        } else {
            $eventType = "end";
            $twitchWebhook->live_since = null;
            $twitchWebhook->offline_since = Carbon::now();
            $twitchWebhook->save();
        }

        if ($twitchWebhook->webhookActions) {
            // TODO: move this (generation of the message embed) to a separate method
            $userInfo = Twitch::getUserById(intval($data["user_id"]))->data[0];
            $gameInfo = null;

            if ($data["game_id"]) {
                $gameInfo = Twitch::getGameById(intval($data["game_id"]))->data[0];
            }

            $embed = new DiscordEmbedMessage();
            $embed->setUsername(config("app.name"))
                ->setAvatar("https://tse1.mm.bing.net/th?id=OIP.kncd96A4OjouPDpT8ymkIAHaHa&pid=Api&f=1")
                ->setContent("¡Hola @everyone! " . $data["user_name"] . " está ahora en directo https://twitch.tv/" . $userInfo->login . " ! Ven a verle :wink:!")
                // embed
                ->setColor(hexdec(substr("#9147ff", 1)))
                ->setAuthorName($userInfo->display_name)
                ->setAuthorIcon($userInfo->profile_image_url)
                ->setThumbnail($userInfo->profile_image_url)
                ->setImage(str_replace(["{width}", "{height}"], [1280, 720], $data["thumbnail_url"]) . "?cache=" . Carbon::now()->timestamp)
                ->setTitle($data["title"])
                ->setUrl("https://twitch.tv/$userInfo->login")
                ->addField("Juego", $gameInfo ? $gameInfo->name : "─", true)
                ->addField("Espectadores", $data["viewer_count"], true);

            foreach($twitchWebhook->webhookActions as $hookAction) {

                // TODO: change the url with the appropiate one
                $webhook = new DiscordWebhook($hookAction->discord_hook_url);
                $webhook->send($embed);

            }

        }

        return response("Ok", 200);
    }

    // TODO: implement the method
    public function subscription(Request $request) {
        if (!$this->verifySecret()) {
            abort(403);
        }

        return "subscription";
    }

    // TODO: verify that the method works ok
    private function verifySecret(String $secret = null) {
        if ($secret) {
            $X_HUB_SIGNATURE = null;

            if (isset($_SERVER["HTTP_X_HUB_SIGNATURE"])) {
                $X_HUB_SIGNATURE = $_SERVER["HTTP_X_HUB_SIGNATURE"];
            }

            $data = file_get_contents("php://input");
            $hmac = hash_hmac("sha256", $data, $secret);

            return $hmac == $X_HUB_SIGNATURE;
        }

        return true;
    }
}
