/*
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 "data/data_star_gift.h"

#include "api/api_premium.h"
#include "apiwrap.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "lang/lang_tag.h"
#include "main/main_session.h"
#include "ui/controls/ton_common.h"
#include "ui/text/text_utilities.h"
#include "styles/style_credits.h"

namespace Data {
namespace {

constexpr auto kMyGiftsPerPage = 50;
constexpr auto kResaleGiftsPerPage = 50;
constexpr auto kCraftGiftsPerPage = 50;

[[nodiscard]] MTPStarGiftAttributeId AttributeToTL(GiftAttributeId id) {
	switch (id.type) {
	case GiftAttributeIdType::Backdrop:
		return MTP_starGiftAttributeIdBackdrop(
			MTP_int(int32(uint32(id.value))));
	case GiftAttributeIdType::Model:
		return MTP_starGiftAttributeIdModel(MTP_long(id.value));
	case GiftAttributeIdType::Pattern:
		return MTP_starGiftAttributeIdPattern(MTP_long(id.value));
	}
	Unexpected("Invalid attribute id type");
}

[[nodiscard]] GiftAttributeId FromTL(const MTPStarGiftAttributeId &id) {
	return id.match([&](const MTPDstarGiftAttributeIdBackdrop &data) {
		return GiftAttributeId{
			.value = uint64(uint32(data.vbackdrop_id().v)),
			.type = GiftAttributeIdType::Backdrop,
		};
	}, [&](const MTPDstarGiftAttributeIdModel &data) {
		return GiftAttributeId{
			.value = data.vdocument_id().v,
			.type = GiftAttributeIdType::Model,
		};
	}, [&](const MTPDstarGiftAttributeIdPattern &data) {
		return GiftAttributeId{
			.value = data.vdocument_id().v,
			.type = GiftAttributeIdType::Pattern,
		};
	});
}

} // namespace

QString UniqueGiftName(const UniqueGift &gift) {
	return UniqueGiftName(gift.title, gift.number);
}

QString UniqueGiftName(const QString &title, int number) {
	return title + u" #"_q + Lang::FormatCountDecimal(number);
}

QString UniqueGiftRarityText(UniqueGiftRarity type) {
	switch (type) {
	case UniqueGiftRarity::Uncommon:
		return tr::lng_gift_uncommon_tag(tr::now);
	case UniqueGiftRarity::Rare:
		return tr::lng_gift_rare_tag(tr::now);
	case UniqueGiftRarity::Epic:
		return tr::lng_gift_epic_tag(tr::now);
	case UniqueGiftRarity::Legendary:
		return tr::lng_gift_legendary_tag(tr::now);
	}
	return QString();
}

UniqueGiftRarityColors UniqueGiftRarityBadgeColors(UniqueGiftRarity type) {
	constexpr auto kBgOpacity = 0.15;
	const auto base = [&] {
		switch (type) {
		case UniqueGiftRarity::Uncommon: return QColor(0x40, 0xA9, 0x20);
		case UniqueGiftRarity::Rare: return QColor(0x11, 0xAA, 0xBE);
		case UniqueGiftRarity::Epic: return QColor(0x95, 0x5C, 0xDB);
		case UniqueGiftRarity::Legendary: return QColor(0xBF, 0x76, 0x00);
		}
		Unexpected("Invalid rarity type.");
	}();
	auto bg = base;
	bg.setAlphaF(kBgOpacity);
	return { bg, base };
}

QString UniqueGiftAttributeText(const UniqueGiftAttribute &attribute) {
	const auto type = attribute.rarityType();
	if (type != UniqueGiftRarity::Default) {
		return UniqueGiftRarityText(type);
	}
	const auto permille = attribute.rarityPermille();
	return (permille > 0)
		? (QString::number(permille / 10.) + '%')
		: u"< 0.1%"_q;
}

bool UniqueGiftAttributeHasSpecialRarity(const UniqueGiftAttribute &attribute) {
	return attribute.rarityType() != UniqueGiftRarity::Default;
}

CreditsAmount UniqueGiftResaleStars(const UniqueGift &gift) {
	return CreditsAmount(gift.starsForResale);
}

CreditsAmount UniqueGiftResaleTon(const UniqueGift &gift) {
	return CreditsAmount(
		gift.nanoTonForResale / Ui::kNanosInOne,
		gift.nanoTonForResale % Ui::kNanosInOne,
		CreditsType::Ton);
}

CreditsAmount UniqueGiftResaleAsked(const UniqueGift &gift) {
	return gift.onlyAcceptTon
		? UniqueGiftResaleTon(gift)
		: UniqueGiftResaleStars(gift);
}

TextWithEntities FormatGiftResaleStars(const UniqueGift &gift) {
	return Ui::Text::IconEmoji(
		&st::starIconEmoji
	).append(Lang::FormatCountDecimal(gift.starsForResale));
}

TextWithEntities FormatGiftResaleTon(const UniqueGift &gift) {
	return Ui::Text::IconEmoji(
		&st::tonIconEmoji
	).append(Lang::FormatCreditsAmountDecimal(UniqueGiftResaleTon(gift)));
}

TextWithEntities FormatGiftResaleAsked(const UniqueGift &gift) {
	return gift.onlyAcceptTon
		? FormatGiftResaleTon(gift)
		: FormatGiftResaleStars(gift);
}

GiftAttributeId IdFor(const UniqueGiftBackdrop &value) {
	return {
		.value = uint64(uint32(value.id)),
		.type = GiftAttributeIdType::Backdrop,
	};
}

GiftAttributeId IdFor(const UniqueGiftModel &value) {
	return {
		.value = value.document->id,
		.type = GiftAttributeIdType::Model,
	};
}

GiftAttributeId IdFor(const UniqueGiftPattern &value) {
	return {
		.value = value.document->id,
		.type = GiftAttributeIdType::Pattern,
	};
}

rpl::producer<MyGiftsDescriptor> MyUniqueGiftsSlice(
		not_null<Main::Session*> session,
		MyUniqueType type,
		QString offset) {
	return [=](auto consumer) {
		using Flag = MTPpayments_GetSavedStarGifts::Flag;
		const auto user = session->user();
		const auto requestId = session->api().request(
			MTPpayments_GetSavedStarGifts(
			MTP_flags(Flag::f_exclude_upgradable
				| Flag::f_exclude_unupgradable
				| Flag::f_exclude_unlimited
				| ((type == MyUniqueType::OnlyOwned)
					? Flag::f_exclude_hosted
					: Flag())),
			user->input(),
			MTP_int(0), // collection_id
			MTP_string(offset),
			MTP_int(kMyGiftsPerPage)
		)).done([=](const MTPpayments_SavedStarGifts &result) {
			auto gifts = MyGiftsDescriptor();
			const auto &data = result.data();
			if (const auto next = data.vnext_offset()) {
				gifts.offset = qs(*next);
			}

			const auto owner = &session->data();
			owner->processUsers(data.vusers());
			owner->processChats(data.vchats());

			gifts.list.reserve(data.vgifts().v.size());
			for (const auto &gift : data.vgifts().v) {
				if (auto parsed = Api::FromTL(user, gift)) {
					gifts.list.push_back(std::move(*parsed));
				}
			}
			consumer.put_next(std::move(gifts));
			consumer.put_done();
		}).fail([=] {
			consumer.put_next({});
			consumer.put_done();
		}).send();

		auto lifetime = rpl::lifetime();
		lifetime.add([=] { session->api().request(requestId).cancel(); });
		return lifetime;
	};
}

rpl::producer<ResaleGiftsDescriptor> ResaleGiftsSlice(
		not_null<Main::Session*> session,
		uint64 giftId,
		ResaleGiftsFilter filter,
		QString offset) {
	return [=](auto consumer) {
		using Flag = MTPpayments_GetResaleStarGifts::Flag;
		const auto requestId = session->api().request(
			MTPpayments_GetResaleStarGifts(
				MTP_flags(Flag::f_attributes_hash
					| ((filter.sort == ResaleGiftsSort::Price)
						? Flag::f_sort_by_price
						: (filter.sort == ResaleGiftsSort::Number)
						? Flag::f_sort_by_num
						: Flag())
					| (filter.attributes.empty()
						? Flag()
						: Flag::f_attributes)
					| (filter.forCraft ? Flag::f_for_craft : Flag())
					| (filter.starsOnly ? Flag::f_stars_only : Flag())),
				MTP_long(filter.attributesHash),
				MTP_long(giftId),
				MTP_vector_from_range(filter.attributes
					| ranges::views::transform(AttributeToTL)),
				MTP_string(offset),
				MTP_int(kResaleGiftsPerPage)
			)).done([=](const MTPpayments_ResaleStarGifts &result) {
			const auto &data = result.data();
			session->data().processUsers(data.vusers());
			session->data().processChats(data.vchats());

			auto info = ResaleGiftsDescriptor{
				.giftId = giftId,
				.offset = qs(data.vnext_offset().value_or_empty()),
				.count = data.vcount().v,
			};
			const auto &list = data.vgifts().v;
			info.list.reserve(list.size());
			for (const auto &entry : list) {
				if (auto gift = Api::FromTL(session, entry)) {
					info.list.push_back(std::move(*gift));
				}
			}
			info.attributesHash = data.vattributes_hash().value_or_empty();
			const auto &attributes = data.vattributes()
				? data.vattributes()->v
				: QVector<MTPStarGiftAttribute>();
			const auto &counters = data.vcounters()
				? data.vcounters()->v
				: QVector<MTPStarGiftAttributeCounter>();
			auto counts = base::flat_map<GiftAttributeId, int>();
			counts.reserve(counters.size());
			for (const auto &counter : counters) {
				const auto &data = counter.data();
				counts.emplace(FromTL(data.vattribute()), data.vcount().v);
			}
			const auto count = [&](GiftAttributeId id) {
				const auto i = counts.find(id);
				return i != end(counts) ? i->second : 0;
			};
			info.models.reserve(attributes.size());
			info.patterns.reserve(attributes.size());
			info.backdrops.reserve(attributes.size());
			for (const auto &attribute : attributes) {
				attribute.match([&](const MTPDstarGiftAttributeModel &data) {
					const auto parsed = Api::FromTL(session, data);
					info.models.push_back({ parsed, count(IdFor(parsed)) });
				}, [&](const MTPDstarGiftAttributePattern &data) {
					const auto parsed = Api::FromTL(session, data);
					info.patterns.push_back({ parsed, count(IdFor(parsed)) });
				}, [&](const MTPDstarGiftAttributeBackdrop &data) {
					const auto parsed = Api::FromTL(data);
					info.backdrops.push_back({ parsed, count(IdFor(parsed)) });
				}, [](const MTPDstarGiftAttributeOriginalDetails &data) {
				});
			}
			consumer.put_next(std::move(info));
			consumer.put_done();
		}).fail([=](const MTP::Error &error) {
			consumer.put_next({});
			consumer.put_done();
		}).send();

		auto lifetime = rpl::lifetime();
		lifetime.add([=] { session->api().request(requestId).cancel(); });
		return lifetime;
	};
}

rpl::producer<CraftGiftsDescriptor> CraftGiftsSlice(
		not_null<Main::Session*> session,
		uint64 giftId,
		QString offset) {
	return [=](auto consumer) {
		const auto requestId = session->api().request(
			MTPpayments_GetCraftStarGifts(
				MTP_long(giftId),
				MTP_string(offset),
				MTP_int(kCraftGiftsPerPage))
		).done([=](const MTPpayments_SavedStarGifts &result) {
			const auto user = session->user();
			const auto owner = &session->data();
			const auto &data = result.data();
			owner->processUsers(data.vusers());
			owner->processChats(data.vchats());

			auto info = CraftGiftsDescriptor{
				.giftId = giftId,
				.offset = qs(data.vnext_offset().value_or_empty()),
				.count = data.vcount().v,
			};
			info.list.reserve(data.vgifts().v.size());
			for (const auto &gift : data.vgifts().v) {
				if (auto parsed = Api::FromTL(user, gift)) {
					info.list.push_back(std::move(*parsed));
				}
			}
			consumer.put_next(std::move(info));
			consumer.put_done();
		}).fail([=](const MTP::Error &error) {
			consumer.put_next({});
			consumer.put_done();
		}).send();

		auto lifetime = rpl::lifetime();
		lifetime.add([=] { session->api().request(requestId).cancel(); });
		return lifetime;
	};
}

} // namespace Data
