import $ from "jquery";
import _ from "lodash";

import render_giphy_picker from "../templates/giphy_picker.hbs";
import render_giphy_picker_mobile from "../templates/giphy_picker_mobile.hbs";

import * as blueslip from "./blueslip";
import * as compose_ui from "./compose_ui";
import {media_breakpoints_num} from "./css_variables";
import {page_params} from "./page_params";
import * as popovers from "./popovers";
import * as rows from "./rows";
import * as ui_util from "./ui_util";

let giphy_fetch;
let search_term = "";
let gifs_grid;
let $active_popover_element;

// Only used if popover called from edit message, otherwise it is `undefined`.
let edit_message_id;

export function is_popped_from_edit_messsage() {
    return $active_popover_element && edit_message_id !== undefined;
}

export function focus_current_edit_message() {
    $(`#edit_form_${CSS.escape(edit_message_id)} .message_edit_content`).trigger("focus");
}

export function is_giphy_enabled() {
    return (
        page_params.giphy_api_key !== "" &&
        page_params.realm_giphy_rating !== page_params.giphy_rating_options.disabled.id
    );
}

// Approximate width and height of
// giphy popover as computed by chrome
// + 25px;
const APPROX_HEIGHT = 350;
const APPROX_WIDTH = 300;

export function update_giphy_rating() {
    if (
        page_params.realm_giphy_rating === page_params.giphy_rating_options.disabled.id ||
        page_params.giphy_api_key === ""
    ) {
        $(".compose_gif_icon").hide();
    } else {
        $(".compose_gif_icon").show();
    }
}

function get_rating() {
    const options = page_params.giphy_rating_options;
    for (const rating in page_params.giphy_rating_options) {
        if (options[rating].id === page_params.realm_giphy_rating) {
            return rating;
        }
    }

    // The below should never run unless a server bug allowed a
    // `giphy_rating` value not present in `giphy_rating_options`.
    blueslip.error("Invalid giphy_rating value: " + page_params.realm_giphy_rating);
    return "g";
}

async function renderGIPHYGrid(targetEl) {
    const {renderGrid} = await import(/* webpackChunkName: "giphy-sdk" */ "@giphy/js-components");
    const {GiphyFetch} = await import(/* webpackChunkName: "giphy-sdk" */ "@giphy/js-fetch-api");

    if (giphy_fetch === undefined) {
        giphy_fetch = new GiphyFetch(page_params.giphy_api_key);
    }

    function fetchGifs(offset) {
        const config = {
            offset,
            limit: 25,
            rating: get_rating(),
            // We don't pass random_id here, for privacy reasons.
        };
        if (search_term === "") {
            // Get the trending gifs by default.
            return giphy_fetch.trending(config);
        }
        return giphy_fetch.search(search_term, config);
    }

    const render = () =>
        // See https://github.com/Giphy/giphy-js/blob/master/packages/components/README.md#grid
        // for detailed documentation.
        renderGrid(
            {
                width: 300,
                fetchGifs,
                columns: 3,
                gutter: 6,
                noLink: true,
                // Hide the creator attribution that appears over a
                // GIF; nice in principle but too distracting.
                hideAttribution: true,
                onGifClick: (props) => {
                    let $textarea = $("#compose-textarea");
                    if (edit_message_id !== undefined) {
                        $textarea = $(
                            `#edit_form_${CSS.escape(edit_message_id)} .message_edit_content`,
                        );
                    }

                    compose_ui.insert_syntax_and_focus(
                        `[](${props.images.downsized_medium.url})`,
                        $textarea,
                    );
                    hide_giphy_popover();
                },
                onGifVisible: (gif, e) => {
                    // Set tabindex for all the GIFs that
                    // are visible to the user. This allows
                    // user to navigate the GIFs using tab.
                    // TODO: Remove this after https://github.com/Giphy/giphy-js/issues/174
                    // is closed.
                    e.target.tabIndex = 0;
                },
            },
            targetEl,
        );

    // Limit the rate at which we do queries to the GIPHY API to
    // one per 300ms, in line with animation timing, basically to avoid
    // content appearing while the user is typing.
    const resizeRender = _.throttle(render, 300);
    window.addEventListener("resize", resizeRender, false);
    const remove = render();
    return {
        remove: () => {
            remove();
            window.removeEventListener("resize", resizeRender, false);
        },
    };
}

async function update_grid_with_search_term() {
    if (!gifs_grid) {
        return;
    }

    const $search_elem = $("#giphy-search-query");
    // GIPHY popover may have been hidden by the
    // time this function is called.
    if ($search_elem.length) {
        search_term = $search_elem[0].value;
        gifs_grid.remove();
        gifs_grid = await renderGIPHYGrid($("#giphy_grid_in_popover .giphy-content")[0]);
        return;
    }

    // Set to undefined to stop searching.
    gifs_grid = undefined;
}

export function hide_giphy_popover() {
    // Returns `true` if the popover was open.
    if ($active_popover_element) {
        // We need to destroy the popover because when
        // we hide it, bootstrap popover
        // library removes `giphy-content` element
        // as part of cleaning up everything inside
        // `popover-content`, so we need to reinitialize
        // the popover by destroying it.
        $active_popover_element.popover("destroy");
        $active_popover_element = undefined;
        edit_message_id = undefined;
        gifs_grid = undefined;
        return true;
    }
    return false;
}

function get_popover_content() {
    if (window.innerWidth <= media_breakpoints_num.md) {
        // Show as modal in the center for small screens.
        return render_giphy_picker_mobile();
    }
    return render_giphy_picker();
}

function get_popover_placement() {
    let placement = popovers.compute_placement(
        $active_popover_element,
        APPROX_HEIGHT,
        APPROX_WIDTH,
        true,
    );

    if (placement === "viewport_center") {
        // For legacy reasons `compute_placement` actually can
        // return `viewport_center` which used to place popover in
        // the center of the screen, but bootstrap doesn't actually
        // support that and we already handle it on small screen sizes
        // by placing it in center using `popover-flex`.
        placement = "left";
    }

    return placement;
}

export function initialize() {
    $("body").on("keydown", ".giphy-gif", ui_util.convert_enter_to_click);
    $("body").on("keydown", ".compose_gif_icon", ui_util.convert_enter_to_click);

    $("body").on("click", "#giphy_search_clear", async (e) => {
        e.stopPropagation();
        $("#giphy-search-query").val("");
        await update_grid_with_search_term();
    });

    $("body").on("click", ".compose_gif_icon", (e) => {
        e.preventDefault();
        e.stopPropagation();

        const compose_click_target = compose_ui.get_compose_click_target(e);
        if ($active_popover_element && $active_popover_element.get()[0] === compose_click_target) {
            // Hide giphy popover if already active.
            hide_giphy_popover();
            return;
        }
        popovers.hide_all();

        const $elt = $(compose_click_target);
        if ($elt.parents(".message_edit_form").length === 1) {
            // Store message id in global variable edit_message_id so that
            // its value can be further used to correctly find the message textarea element.
            edit_message_id = rows.id($elt.parents(".message_row"));
        } else {
            edit_message_id = undefined;
        }

        $active_popover_element = $elt;
        $active_popover_element.popover({
            animation: true,
            placement: get_popover_placement(),
            html: true,
            trigger: "manual",
            template: get_popover_content(),
            /* Popovers without a content property are not displayed,
             * so we need something here; but we haven't contacted the
             * Giphy API yet to get the actual content to display. */
            content: " ",
        });

        $active_popover_element.popover("show");
        // It takes about 1s for the popover to show; So,
        // we wait for popover to display before rendering GIFs
        // in it, otherwise popover is rendered with empty content.
        const popover_observer = new MutationObserver(async () => {
            if ($("#giphy_grid_in_popover .giphy-content").is(":visible")) {
                popover_observer.disconnect();
                gifs_grid = await renderGIPHYGrid($("#giphy_grid_in_popover .giphy-content")[0]);
            }
        });
        const opts = {attributes: false, childList: true, characterData: false, subtree: true};
        popover_observer.observe(document, opts);

        $("body").on(
            "keyup",
            "#giphy-search-query",
            // Use debounce to create a 300ms interval between
            // every search. This makes the UX of searching pleasant
            // by allowing user to finish typing before search
            // is executed.
            _.debounce(update_grid_with_search_term, 300),
        );

        $(document).one("compose_canceled.zulip compose_finished.zulip", () => {
            hide_giphy_popover();
        });

        // Focus on search box by default.
        // This is specially helpful for users
        // navigating via keyboard.
        $("#giphy-search-query").trigger("focus");
    });
}
