/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "dialogs/dialogs_top_bar_suggestion.h"

#include "api/api_authorizations.h"
#include "api/api_credits.h"
#include "api/api_peer_photo.h"
#include "api/api_premium.h"
#include "apiwrap.h"
#include "base/call_delayed.h"
#include "boxes/star_gift_box.h" // ShowStarGiftBox.
#include "boxes/star_gift_auction_box.h"
#include "core/application.h"
#include "core/click_handler_types.h"
#include "core/ui_integration.h"
#include "data/components/gift_auctions.h"
#include "data/components/promo_suggestions.h"
#include "data/data_birthday.h"
#include "data/data_changes.h"
#include "data/data_peer_values.h" // Data::AmPremiumValue.
#include "data/data_session.h"
#include "data/data_user.h"
#include "dialogs/ui/dialogs_top_bar_suggestion_content.h"
#include "history/view/history_view_group_call_bar.h"
#include "info/profile/info_profile_values.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "settings/sections/settings_active_sessions.h"
#include "settings/settings_credits_graphics.h"
#include "settings/sections/settings_premium.h"
#include "ui/boxes/confirm_box.h"
#include "ui/controls/userpic_button.h"
#include "ui/effects/credits_graphics.h"
#include "ui/layers/generic_box.h"
#include "ui/rect.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/ui_utility.h"
#include "ui/vertical_list.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_dialogs.h"
#include "styles/style_layers.h"

namespace Dialogs {
namespace {

[[nodiscard]] not_null<Window::SessionController*> FindSessionController(
		not_null<Ui::RpWidget*> widget) {
	const auto window = Core::App().findWindow(widget);
	Assert(window != nullptr);
	return window->sessionController();
}

[[nodiscard]] QString FormatAuthInfo(const Data::UnreviewedAuth &auth) {
	const auto location = auth.location.isEmpty()
		? QString()
		: "\U0001F30D " + auth.location;
	const auto device = auth.device.isEmpty()
		? QString()
		: "\U0001F4F1 " + auth.device;

	if (!location.isEmpty() && !device.isEmpty()) {
		return location + " (" + device + ")";
	} else if (!location.isEmpty()) {
		return location;
	} else if (!device.isEmpty()) {
		return device;
	}
	return QString();
}

void ShowAuthToast(
		not_null<Ui::RpWidget*> parent,
		not_null<Main::Session*> session,
		const std::vector<Data::UnreviewedAuth> &list,
		bool confirmed) {
	if (confirmed) {
		auto text = tr::lng_unconfirmed_auth_confirmed_message(
			tr::now,
			lt_link,
			tr::link(tr::lng_settings_sessions_title(tr::now)),
			tr::rich);
		auto filter = [=](
				ClickHandlerPtr handler,
				Qt::MouseButton button) {
			if (const auto controller = FindSessionController(parent)) {
				session->api().authorizations().reload();
				controller->showSettings(Settings::SessionsId());
				return false;
			}
			return true;
		};
		Ui::Toast::Show(parent->window(), Ui::Toast::Config{
			.title = tr::lng_unconfirmed_auth_confirmed(tr::now),
			.text = std::move(text),
			.filter = std::move(filter),
			.duration = crl::time(5000),
		});
	} else {
		auto messageText = QString();
		if (list.size() == 1) {
			messageText = tr::lng_unconfirmed_auth_denied_single(
				tr::now,
				lt_country,
				FormatAuthInfo(list.front()));
		} else {
			auto authList = QString('\n');
			for (auto i = 0; i < std::min(int(list.size()), 10); ++i) {
				const auto info = FormatAuthInfo(list[i]);
				if (!info.isEmpty()) {
					authList += "• " + info + "\n";
				}
			}
			messageText = tr::lng_unconfirmed_auth_denied_multiple(
				tr::now,
				lt_country,
				authList);
		}
		if (const auto controller = FindSessionController(parent)) {
			const auto count = float64(list.size());
			controller->show(Box(ShowAuthDeniedBox, count, messageText));
		}
	}
}

constexpr auto kSugSetBirthday = "BIRTHDAY_SETUP"_cs;
constexpr auto kSugBirthdayContacts = "BIRTHDAY_CONTACTS_TODAY"_cs;
constexpr auto kSugPremiumAnnual = "PREMIUM_ANNUAL"_cs;
constexpr auto kSugPremiumUpgrade = "PREMIUM_UPGRADE"_cs;
constexpr auto kSugPremiumRestore = "PREMIUM_RESTORE"_cs;
constexpr auto kSugPremiumGrace = "PREMIUM_GRACE"_cs;
constexpr auto kSugSetUserpic = "USERPIC_SETUP"_cs;
constexpr auto kSugLowCreditsSubs = "STARS_SUBSCRIPTION_LOW_BALANCE"_cs;


} // namespace

rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
		not_null<Ui::RpWidget*> parent,
		not_null<Main::Session*> session,
		rpl::producer<bool> outerWrapToggleValue) {
	return [=, outerWrapToggleValue = rpl::duplicate(outerWrapToggleValue)](
			auto consumer) {
		auto lifetime = rpl::lifetime();

		struct Toggle {
			bool value = false;
			anim::type type;
		};

		struct State {
			TopBarSuggestionContent *content = nullptr;
			Ui::SlideWrap<Ui::VerticalLayout> *unconfirmedWarning = nullptr;
			base::unique_qptr<Ui::SlideWrap<Ui::RpWidget>> wrap;
			rpl::variable<int> leftPadding;
			rpl::variable<Toggle> desiredWrapToggle;
			rpl::variable<bool> outerWrapToggle;
			rpl::lifetime birthdayLifetime;
			rpl::lifetime premiumLifetime;
			rpl::lifetime userpicLifetime;
			rpl::lifetime giftsLifetime;
			rpl::lifetime creditsLifetime;
			rpl::lifetime auctionsLifetime;
			std::unique_ptr<Api::CreditsHistory> creditsHistory;
		};

		const auto state = lifetime.make_state<State>();
		state->outerWrapToggle = rpl::duplicate(outerWrapToggleValue);
		state->leftPadding = rpl::variable<int>(
			rpl::single(st::dialogsTopBarLeftPadding));
		const auto ensureContent = [=] {
			if (!state->content) {
				const auto window = FindSessionController(parent);
				state->content = Ui::CreateChild<TopBarSuggestionContent>(
					parent,
					[=] { return window->isGifPausedAtLeastFor(
						Window::GifPauseReason::Layer); });
				rpl::combine(
					parent->widthValue(),
					state->content->desiredHeightValue()
				) | rpl::on_next([=](int width, int height) {
					state->content->resize(width, height);
				}, state->content->lifetime());
			}
		};
		const auto ensureWrap = [=](not_null<Ui::RpWidget*> child) {
			if (!state->wrap) {
				state->wrap
					= base::make_unique_q<Ui::SlideWrap<Ui::RpWidget>>(
						parent,
						object_ptr<Ui::RpWidget>::fromRaw(child));
				state->desiredWrapToggle.force_assign(
					Toggle{ false, anim::type::instant });
			}
		};

		const auto setLeftPaddingRelativeTo = [=](
				not_null<TopBarSuggestionContent*> content,
				not_null<Ui::RpWidget*> relativeTo) {
			content->setLeftPadding(state->leftPadding.value(
				) | rpl::map([w = relativeTo->width()](int padding) {
					return w + padding * 2;
				}));
		};

		const auto processCurrentSuggestion = [=](auto repeat) -> void {
			state->birthdayLifetime.destroy();
			state->premiumLifetime.destroy();
			state->userpicLifetime.destroy();
			state->giftsLifetime.destroy();
			state->creditsLifetime.destroy();
			state->auctionsLifetime.destroy();

			if (!session->api().authorizations().unreviewed().empty()) {
				state->content = nullptr;
				state->wrap = nullptr;
				const auto &list
					= session->api().authorizations().unreviewed();
				const auto hashes = ranges::views::all(
					list
				) | ranges::views::transform([](const auto &auth) {
					return auth.hash;
				}) | ranges::to_vector;

				const auto content = CreateUnconfirmedAuthContent(
					parent,
					list,
					[=](bool confirmed) {
						ShowAuthToast(parent, session, list, confirmed);
						session->api().authorizations().review(
							hashes,
							confirmed);
					});
				ensureWrap(content);
				const auto wasUnconfirmedWarning = state->unconfirmedWarning;
				state->unconfirmedWarning = content;
				state->desiredWrapToggle.force_assign(Toggle{
					true,
					(state->unconfirmedWarning != wasUnconfirmedWarning)
						? anim::type::instant
						: anim::type::normal,
				});
				return;
			} else {
				if (state->unconfirmedWarning) {
					state->unconfirmedWarning = nullptr;
					state->wrap = nullptr;
				}
			}

			ensureContent();
			ensureWrap(state->content);
			const auto content = state->content;
			const auto wrap = state->wrap.get();
			using RightIcon = TopBarSuggestionContent::RightIcon;
			const auto promo = &session->promoSuggestions();
			const auto auctions = &session->giftAuctions();
			if (auctions->hasActive()) {
				using namespace Data;
				struct Button {
					rpl::variable<TextWithEntities> text;
					Fn<void()> callback;
					base::has_weak_ptr guard;
				};
				auto &lifetime = state->auctionsLifetime;
				const auto button = lifetime.template make_state<Button>();
				const auto window = FindSessionController(parent);
				auctions->active(
				) | rpl::on_next([=](ActiveAuctions &&active) {
					const auto empty = active.list.empty();
					state->desiredWrapToggle.force_assign(
						Toggle{ !empty, anim::type::normal });
					if (empty) {
						return;
					}

					auto text = Ui::ActiveAuctionsState(active);
					const auto textColorOverride = text.someOutbid
						? st::attentionButtonFg->c
						: std::optional<QColor>();
					content->setContent(
						Ui::ActiveAuctionsTitle(active),
						std::move(text.text),
						Core::TextContext({ .session = session }),
						textColorOverride);
					button->text = Ui::ActiveAuctionsButton(active);
					button->callback = Ui::ActiveAuctionsCallback(
						window,
						active);
				}, state->auctionsLifetime);
				const auto callback = crl::guard(&button->guard, [=] {
					button->callback();
				});
				content->setRightButton(button->text.value(), callback);
				content->setClickedCallback(callback);
				content->setLeftPadding(state->leftPadding.value());
				state->desiredWrapToggle.force_assign(
					Toggle{ true, anim::type::normal });
				return;
			} else if (const auto custom = promo->custom()) {
				content->setRightIcon(RightIcon::Close);
				content->setLeftPadding(state->leftPadding.value());
				content->setClickedCallback([=] {
					const auto controller = FindSessionController(parent);
					UrlClickHandler::Open(
						custom->url,
						QVariant::fromValue(ClickHandlerContext{
							.sessionWindow = base::make_weak(controller),
						}));
				});
				content->setHideCallback([=] {
					promo->dismiss(custom->suggestion);
					repeat(repeat);
				});

				content->setContent(
					custom->title,
					custom->description,
					Core::TextContext({ .session = session }));
				state->desiredWrapToggle.force_assign(
					Toggle{ true, anim::type::normal });
				return;
			} else if (session->premiumCanBuy()
				&& promo->current(kSugPremiumGrace.utf8())) {
				content->setRightIcon(RightIcon::Close);
				content->setLeftPadding(state->leftPadding.value());
				content->setClickedCallback([=] {
					const auto controller = FindSessionController(parent);
					UrlClickHandler::Open(
						u"https://t.me/premiumbot?start=status"_q,
						QVariant::fromValue(ClickHandlerContext{
							.sessionWindow = base::make_weak(controller),
						}));
				});
				content->setHideCallback([=] {
					promo->dismiss(kSugPremiumGrace.utf8());
					repeat(repeat);
				});
				content->setContent(
					tr::lng_dialogs_suggestions_premium_grace_title(
						tr::now,
						tr::bold),
					tr::lng_dialogs_suggestions_premium_grace_about(
						tr::now,
						TextWithEntities::Simple));
				state->desiredWrapToggle.force_assign(
					Toggle{ true, anim::type::normal });
				return;
			} else if (session->premiumCanBuy()
				&& promo->current(kSugLowCreditsSubs.utf8())) {
				state->creditsHistory = std::make_unique<Api::CreditsHistory>(
					session->user(),
					false,
					false);
				const auto show = [=](
						const QString &peers,
						uint64 needed,
						uint64 whole) {
					if (whole > needed) {
						return;
					}
					content->setRightIcon(RightIcon::Close);
					content->setLeftPadding(state->leftPadding.value());
					content->setClickedCallback([=] {
						const auto controller = FindSessionController(parent);
						controller->uiShow()->show(Box(
							Settings::SmallBalanceBox,
							controller->uiShow(),
							needed,
							Settings::SmallBalanceSubscription{ peers },
							[=] {
								promo->dismiss(kSugLowCreditsSubs.utf8());
								repeat(repeat);
							}));
					});
					content->setHideCallback([=] {
						promo->dismiss(kSugLowCreditsSubs.utf8());
						repeat(repeat);
					});

					content->setContent(
						tr::lng_dialogs_suggestions_credits_sub_low_title(
							tr::now,
							lt_count,
							float64(needed - whole),
							lt_emoji,
							Ui::MakeCreditsIconEntity(),
							lt_channels,
							{ peers },
							tr::bold),
						tr::lng_dialogs_suggestions_credits_sub_low_about(
							tr::now,
							TextWithEntities::Simple),
						Ui::MakeCreditsIconContext(
							content->contentTitleSt().font->height,
							1));
					state->desiredWrapToggle.force_assign(
						Toggle{ true, anim::type::normal });
				};
				session->credits().load();
				state->creditsLifetime.destroy();
				session->credits().balanceValue() | rpl::on_next([=] {
					state->creditsLifetime.destroy();
					state->creditsHistory->requestSubscriptions(
						Data::CreditsStatusSlice::OffsetToken(),
						[=](Data::CreditsStatusSlice slice) {
							state->creditsHistory = nullptr;
							auto peers = QStringList();
							auto credits = uint64(0);
							for (const auto &entry : slice.subscriptions) {
								if (entry.barePeerId) {
									const auto peer = session->data().peer(
										PeerId(entry.barePeerId));
									peers.append(peer->name());
									credits += entry.subscription.credits;
								}
							}
							show(
								peers.join(", "),
								credits,
								session->credits().balance().whole());
						},
						true);
				}, state->creditsLifetime);

				return;
			} else if (session->premiumCanBuy()
				&& promo->current(kSugBirthdayContacts.utf8())) {
				promo->requestContactBirthdays(crl::guard(content, [=] {
					const auto users = promo->knownBirthdaysToday().value_or(
						std::vector<UserId>());
					if (users.empty()) {
						repeat(repeat);
						return;
					}

					const auto controller = FindSessionController(parent);
					const auto isSingle = users.size() == 1;
					const auto first = session->data().user(users.front());
					content->setRightIcon(RightIcon::Close);
					content->setClickedCallback([=] {
						if (isSingle) {
							Ui::ShowStarGiftBox(controller, first);
						} else {
							Ui::ChooseStarGiftRecipient(controller);
						}
					});
					content->setHideCallback([=] {
						promo->dismiss(kSugBirthdayContacts.utf8());
						controller->showToast(
							tr::lng_dialogs_suggestions_birthday_contact_dismiss(
								tr::now));
						repeat(repeat);
					});
					auto title = isSingle
						? tr::lng_dialogs_suggestions_birthday_contact_title(
							tr::now,
							lt_text,
							{ first->shortName() },
							tr::rich)
						: tr::lng_dialogs_suggestions_birthday_contacts_title(
							tr::now,
							lt_count,
							users.size(),
							tr::rich);
					auto text = isSingle
						? tr::lng_dialogs_suggestions_birthday_contact_about(
							tr::now,
							TextWithEntities::Simple)
						: tr::lng_dialogs_suggestions_birthday_contacts_about(
							tr::now,
							TextWithEntities::Simple);
					content->setContent(std::move(title), std::move(text));
					state->giftsLifetime.destroy();
					if (!isSingle) {
						struct UserViews {
							std::vector<HistoryView::UserpicInRow> inRow;
							QImage userpics;
							base::unique_qptr<Ui::RpWidget> widget;
						};
						const auto s
							= state->giftsLifetime.template make_state<
								UserViews>();
						s->widget = base::make_unique_q<Ui::RpWidget>(
							content);
						const auto widget = s->widget.get();
						widget->setAttribute(
							Qt::WA_TransparentForMouseEvents);
						content->sizeValue() | rpl::filter_size(
						) | rpl::on_next([=](const QSize &size) {
							widget->resize(size);
							widget->show();
							widget->raise();
						}, widget->lifetime());
						for (const auto &id : users) {
							if (s->inRow.size() >= 3) {
								break;
							}
							if (const auto user = session->data().user(id)) {
								s->inRow.push_back({ .peer = user });
							}
						}
						widget->paintRequest() | rpl::on_next([=] {
							auto p = QPainter(widget);
							if (HistoryView::NeedRegenerateUserpics(
									s->userpics,
									s->inRow)) {
								const auto &st = st::historyCommentsUserpics;
								HistoryView::GenerateUserpicsInRow(
									s->userpics,
									s->inRow,
									st,
									3);
								const auto v = int(users.size() * st.size
									- st.shift);
								content->setLeftPadding(
									state->leftPadding.value(
									) | rpl::map([v](int padding) {
										return padding * 2 + v;
									}));
							}
							p.drawImage(
								state->leftPadding.current(),
								(widget->height()
									- (s->userpics.height()
										/ style::DevicePixelRatio())) / 2,
								s->userpics);
						}, widget->lifetime());
					} else {
						using Ptr = base::unique_qptr<Ui::UserpicButton>;
						const auto ptr
							= state->giftsLifetime.template make_state<Ptr>(
								base::make_unique_q<Ui::UserpicButton>(
									content,
									first,
									st::uploadUserpicButton));
						const auto fake = ptr->get();
						fake->setAttribute(Qt::WA_TransparentForMouseEvents);
						rpl::combine(
							state->leftPadding.value(),
							content->sizeValue() | rpl::filter_size()
						) | rpl::on_next([=](int p, const QSize &s) {
							fake->raise();
							fake->show();
							fake->moveToLeft(
								p,
								(s.height() - fake->height()) / 2);
						}, fake->lifetime());
						setLeftPaddingRelativeTo(content, fake);
					}

					state->desiredWrapToggle.force_assign(
						Toggle{ true, anim::type::normal });
				}));
				return;
			} else if (promo->current(kSugSetBirthday.utf8())
				&& !Data::IsBirthdayToday(session->user()->birthday())) {
				content->setRightIcon(RightIcon::Close);
				content->setLeftPadding(state->leftPadding.value());
				content->setClickedCallback([=] {
					const auto controller = FindSessionController(parent);
					Core::App().openInternalUrl(
						u"internal:edit_birthday:add_privacy"_q,
						QVariant::fromValue(ClickHandlerContext{
							.sessionWindow = base::make_weak(controller),
						}));

					state->birthdayLifetime = Info::Profile::BirthdayValue(
						session->user()
					) | rpl::map(
						Data::IsBirthdayTodayValue
					) | rpl::flatten_latest(
					) | rpl::distinct_until_changed(
					) | rpl::on_next([=] {
						repeat(repeat);
					});
				});
				content->setHideCallback([=] {
					promo->dismiss(kSugSetBirthday.utf8());
					repeat(repeat);
				});
				content->setContent(
					tr::lng_dialogs_suggestions_birthday_title(
						tr::now,
						tr::bold),
					tr::lng_dialogs_suggestions_birthday_about(
						tr::now,
						TextWithEntities::Simple));
				state->desiredWrapToggle.force_assign(
					Toggle{ true, anim::type::normal });
				return;
			} else if (session->premiumPossible() && !session->premium()) {
				const auto isPremiumAnnual = promo->current(
					kSugPremiumAnnual.utf8());
				const auto isPremiumRestore = !isPremiumAnnual
					&& promo->current(kSugPremiumRestore.utf8());
				const auto isPremiumUpgrade = !isPremiumAnnual
					&& !isPremiumRestore
					&& promo->current(kSugPremiumUpgrade.utf8());
				const auto set = [=](QString discount) {
					constexpr auto kMinus = QChar(0x2212);
					const auto &title = isPremiumAnnual
						? tr::lng_dialogs_suggestions_premium_annual_title
						: isPremiumRestore
						? tr::lng_dialogs_suggestions_premium_restore_title
						: tr::lng_dialogs_suggestions_premium_upgrade_title;
					const auto &description = isPremiumAnnual
						? tr::lng_dialogs_suggestions_premium_annual_about
						: isPremiumRestore
						? tr::lng_dialogs_suggestions_premium_restore_about
						: tr::lng_dialogs_suggestions_premium_upgrade_about;
					content->setContent(
						title(
							tr::now,
							lt_text,
							{ discount.replace(kMinus, QChar()) },
							tr::bold),
						description(tr::now, TextWithEntities::Simple));
					content->setClickedCallback([=] {
						const auto controller = FindSessionController(parent);
						Settings::ShowPremium(controller, "dialogs_hint");
						promo->dismiss(isPremiumAnnual
							? kSugPremiumAnnual.utf8()
							: isPremiumRestore
							? kSugPremiumRestore.utf8()
							: kSugPremiumUpgrade.utf8());
						repeat(repeat);
					});
					state->desiredWrapToggle.force_assign(
						Toggle{ true, anim::type::normal });
				};
				if (isPremiumAnnual || isPremiumRestore || isPremiumUpgrade) {
					content->setRightIcon(RightIcon::Arrow);
					content->setLeftPadding(state->leftPadding.value());
					const auto api = &session->api().premium();
					api->statusTextValue() | rpl::on_next([=] {
						for (const auto &o : api->subscriptionOptions()) {
							if (o.months == 12) {
								set(o.discount);
								state->premiumLifetime.destroy();
								return;
							}
						}
					}, state->premiumLifetime);
					api->reload();
					return;
				}
			}
			if (promo->current(kSugSetUserpic.utf8())
				&& !session->user()->userpicPhotoId()) {
				const auto controller = FindSessionController(parent);
				content->setRightIcon(RightIcon::Close);
				const auto upload = Ui::CreateChild<Ui::UserpicButton>(
					content,
					&controller->window(),
					Ui::UserpicButton::Role::ChoosePhoto,
					st::uploadUserpicButton);
				rpl::combine(
					state->leftPadding.value(),
					content->sizeValue() | rpl::filter_size()
				) | rpl::on_next([=](int padding, const QSize &s) {
					upload->raise();
					upload->show();
					upload->moveToLeft(
						padding,
						(s.height() - upload->height()) / 2);
				}, content->lifetime());
				setLeftPaddingRelativeTo(content, upload);
				upload->chosenImages() | rpl::on_next([=](
						Ui::UserpicButton::ChosenImage &&chosen) {
					if (chosen.type == Ui::UserpicButton::ChosenType::Set) {
						session->api().peerPhoto().upload(
							session->user(),
							{
								std::move(chosen.image),
								chosen.markup.documentId,
								chosen.markup.colors,
							});
					}
				}, upload->lifetime());

				state->userpicLifetime = session->changes().peerUpdates(
					session->user(),
					Data::PeerUpdate::Flag::Photo
				) | rpl::on_next([=] {
					if (session->user()->userpicPhotoId()) {
						repeat(repeat);
					}
				});

				content->setHideCallback([=] {
					promo->dismiss(kSugSetUserpic.utf8());
					repeat(repeat);
				});

				content->setClickedCallback([=] {
					const auto syntetic = [=](QEvent::Type type) {
						Ui::SendSynteticMouseEvent(
							upload,
							type,
							Qt::LeftButton,
							upload->mapToGlobal(QPoint(0, 0)));
					};
					syntetic(QEvent::MouseMove);
					syntetic(QEvent::MouseButtonPress);
					syntetic(QEvent::MouseButtonRelease);
				});
				content->setContent(
					tr::lng_dialogs_suggestions_userpics_title(
						tr::now,
						tr::bold),
					tr::lng_dialogs_suggestions_userpics_about(
						tr::now,
						TextWithEntities::Simple));
				state->desiredWrapToggle.force_assign(
					Toggle{ true, anim::type::normal });
				return;
			}
			state->desiredWrapToggle.force_assign(
				Toggle{ false, anim::type::normal });
			base::call_delayed(st::slideWrapDuration * 2, wrap, [=] {
				state->content = nullptr;
				state->wrap = nullptr;
				consumer.put_next(nullptr);
			});
		};

		state->desiredWrapToggle.value() | rpl::combine_previous(
		) | rpl::filter([=] {
			return state->wrap != nullptr;
		}) | rpl::on_next([=](Toggle was, Toggle now) {
			state->wrap->toggle(
				state->outerWrapToggle.current() && now.value,
				(was.value == now.value)
					? anim::type::instant
					: now.type);
		}, lifetime);

		state->outerWrapToggle.value() | rpl::combine_previous(
		) | rpl::filter([=] {
			return state->wrap != nullptr;
		}) | rpl::on_next([=](bool was, bool now) {
			const auto toggle = state->desiredWrapToggle.current();
			state->wrap->toggle(
				toggle.value && now,
				(was == now) ? toggle.type : anim::type::instant);
		}, lifetime);

		rpl::merge(
			session->promoSuggestions().value(),
			session->api().authorizations().unreviewedChanges(),
			Data::AmPremiumValue(session) | rpl::skip(1) | rpl::to_empty,
			session->giftAuctions().hasActiveChanges() | rpl::to_empty
		) | rpl::on_next([=] {
			const auto was = state->wrap.get();
			const auto weak = base::make_weak(was);
			processCurrentSuggestion(processCurrentSuggestion);
			if (was != state->wrap || (was && !weak)) {
				consumer.put_next_copy(state->wrap.get());
			}
		}, lifetime);

		return lifetime;
	};
}

} // namespace Dialogs
