import $ from "jquery";

import * as blueslip from "./blueslip";
import * as message_lists from "./message_lists";
import * as message_store from "./message_store";
import * as rows from "./rows";
import * as timerender from "./timerender";

let is_floating_recipient_bar_showing = false;

function top_offset($elem) {
    return (
        $elem.offset().top -
        $("#message_view_header").safeOuterHeight() -
        $("#navbar_alerts_wrapper").height()
    );
}

export function first_visible_message($bar) {
    // The first truly visible message would be computed using the
    // bottom of the floating recipient bar; but we want the date from
    // the first visible message were the floating recipient bar not
    // displayed, which will always be the first messages whose bottom
    // overlaps the floating recipient bar's space (since you ).

    const $messages = $bar.children(".message_row");
    const $frb = $("#floating_recipient_bar");
    const frb_top = top_offset($frb);
    const frb_bottom = frb_top + $frb.safeOuterHeight();
    let $result;

    for (const message_element of $messages) {
        // The details of this comparison function are sensitive, since we're
        // balancing between three possible bugs:
        //
        // * If we compare against the bottom of the floating
        //   recipient bar, we end up with a bug where if the floating
        //   recipient bar is just above a normal recipient bar while
        //   overlapping a series of 1-line messages, there might be 2
        //   messages occluded by the recipient bar, and we want the
        //   second one, not the first.
        //
        // * If we compare the message bottom against the top of the
        //   floating recipient bar, and the floating recipient bar is
        //   over a "Yesterday/Today" message date row, we might
        //   confusingly have the floating recipient bar display
        //   e.g. "Yesterday" even though all messages in view were
        //   actually sent "Today".
        //
        // * If the the floating recipient bar is over a
        //   between-message groups date separator or similar widget,
        //   there might be no message overlap with the floating
        //   recipient bar.
        //
        // Careful testing of these two corner cases with
        // message_viewport.scrollTop() to set precise scrolling
        // positions determines the value for date_bar_height_offset.

        let $message = $(message_element);
        const message_bottom = top_offset($message) + $message.safeOuterHeight();
        const date_bar_height_offset = 10;

        if (message_bottom > frb_top) {
            $result = $message;
        }

        // Important: This will break if we ever have things that are
        // not message rows inside a recipient_row block.
        $message = $message.next(".message_row");
        if (
            $message.length > 0 &&
            $result &&
            // Before returning a result, we check whether the next
            // message's top is actually below the bottom of the
            // floating recipient bar; this is different from the
            // bottom of our current message because there may be a
            // between-messages date separator row in between.
            top_offset($message) < frb_bottom - date_bar_height_offset
        ) {
            $result = $message;
        }
        if ($result) {
            return $result;
        }
    }

    // If none of the messages are visible, just take the last message.
    return $messages.last();
}

export function get_date($elem) {
    const message_row = first_visible_message($elem);

    if (!message_row || !message_row.length) {
        return undefined;
    }

    const msg_id = rows.id(message_row);

    if (msg_id === undefined) {
        return undefined;
    }

    const message = message_store.get(msg_id);

    if (!message) {
        return undefined;
    }

    const time = new Date(message.timestamp * 1000);
    const today = new Date();
    const rendered_date = timerender.render_date(time, undefined, today)[0].outerHTML;

    return rendered_date;
}

export function relevant_recipient_bars() {
    let elems = [];

    // This line of code does a reverse traversal
    // from the selected message, which should be
    // in the visible part of the feed, but is sometimes
    // not exactly where we want.  The value we get
    // may be be too far up in the feed, but we can
    // deal with that later.
    let $first_elem = candidate_recipient_bar();

    if (!$first_elem) {
        $first_elem = $(".focused_table").find(".recipient_row").first();
    }

    if ($first_elem.length === 0) {
        return [];
    }

    elems.push($first_elem);

    const max_offset = top_offset($("#compose"));
    let header_height = $first_elem.find(".message_header").safeOuterHeight();

    // It's okay to overestimate header_height a bit, as we don't
    // really need an FRB for a section that barely shows.
    header_height += 10;

    function next($elem) {
        $elem = $elem.next();
        while ($elem.length !== 0 && !$elem.hasClass("recipient_row")) {
            $elem = $elem.next();
        }
        return $elem;
    }

    // Now start the forward traversal of recipient bars.
    // We'll stop when we go below the fold.
    let $elem = next($first_elem);

    while ($elem.length) {
        if (top_offset($elem) < header_height) {
            // If we are close to the top, then the prior
            // elements we found are no longer relevant,
            // because either the selected item we started
            // with in our reverse traversal was too high,
            // or there's simply not enough room to draw
            // a recipient bar without it being ugly.
            elems = [];
        }

        if (top_offset($elem) > max_offset) {
            // Out of sight, out of mind!
            // (The element is below the fold, so we stop the
            // traversal.)
            break;
        }

        elems.push($elem);
        $elem = next($elem);
    }

    if (elems.length === 0) {
        blueslip.warn("Unexpected situation--maybe viewport height is very short.");
        return [];
    }

    const items = elems.map(($elem, i) => {
        let date_html;
        let need_frb;

        if (i === 0) {
            date_html = get_date($elem);
            need_frb = top_offset($elem) < 0;
        } else {
            date_html = $elem.find(".recipient_row_date").html();
            need_frb = false;
        }

        const date_text = $(date_html).text();

        // Add title here to facilitate troubleshooting.
        const title = $elem.find(".message_label_clickable").last().attr("title");

        const item = {
            $elem,
            title,
            date_html,
            date_text,
            need_frb,
        };

        return item;
    });

    items[0].show_date = true;

    for (let i = 1; i < items.length; i += 1) {
        items[i].show_date = items[i].date_text !== items[i - 1].date_text;
    }

    for (const item of items) {
        if (!item.need_frb) {
            delete item.date_html;
        }
    }

    return items;
}

export function candidate_recipient_bar() {
    // Find a recipient bar that is close to being onscreen
    // but above the "top".  This function is guaranteed to
    // return **some** recipient bar that is above the fold,
    // if there is one, but it may not be the optimal one if
    // our pointer is messed up.  Starting with the pointer
    // is just an optimization here, and our caller will do
    // a forward traversal and clean up as necessary.
    // In most cases we find the bottom-most of recipient
    // bars that is still above the fold.

    // Start with the pointer's current location.
    const $selected_row = message_lists.current.selected_row();

    if ($selected_row === undefined || $selected_row.length === 0) {
        return undefined;
    }

    let $candidate = rows.get_message_recipient_row($selected_row);
    if ($candidate === undefined) {
        return undefined;
    }

    while ($candidate.length) {
        if ($candidate.hasClass("recipient_row") && top_offset($candidate) < 0) {
            return $candidate;
        }
        // We cannot use .prev(".recipient_row") here, because that
        // returns nothing if the previous element is not a recipient
        // row, rather than finding the first recipient_row.
        $candidate = $candidate.prev();
    }

    return undefined;
}

function show_floating_recipient_bar() {
    if (!is_floating_recipient_bar_showing) {
        $("#floating_recipient_bar").css("visibility", "visible");
        is_floating_recipient_bar_showing = true;
    }
}

let $old_source;
function replace_floating_recipient_bar(source_info) {
    const $source_recipient_bar = source_info.$elem;

    let $new_label;
    let $other_label;
    let $header;

    if ($source_recipient_bar !== $old_source) {
        if ($source_recipient_bar.children(".message_header_stream").length !== 0) {
            $new_label = $("#current_label_stream");
            $other_label = $("#current_label_private_message");
            $header = $source_recipient_bar.children(".message_header_stream");
        } else {
            $new_label = $("#current_label_private_message");
            $other_label = $("#current_label_stream");
            $header = $source_recipient_bar.children(".message_header_private_message");
        }
        $new_label.find(".message_header").replaceWith($header.clone());
        $other_label.css("display", "none");
        $new_label.css("display", "block");
        $new_label.attr("zid", rows.id(rows.first_message_in_group($source_recipient_bar)));

        $new_label.toggleClass("message-fade", $source_recipient_bar.hasClass("message-fade"));
        $old_source = $source_recipient_bar;
    }

    const rendered_date = source_info.date_html || "";

    $("#floating_recipient_bar").find(".recipient_row_date").html(rendered_date);

    show_floating_recipient_bar();
}

export function hide() {
    if (is_floating_recipient_bar_showing) {
        $("#floating_recipient_bar").css("visibility", "hidden");
        is_floating_recipient_bar_showing = false;
    }
}

export function de_clutter_dates(items) {
    for (const item of items) {
        item.$elem.find(".recipient_row_date").toggle(item.show_date);
    }
}

export function update() {
    const items = relevant_recipient_bars();

    if (!items || items.length === 0) {
        hide();
        return;
    }

    de_clutter_dates(items);

    if (!items[0].need_frb) {
        hide();
        return;
    }

    replace_floating_recipient_bar(items[0]);
}
