/*
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 "info/feed/info_feed_channels_controllers.h"

#include "data/data_feed.h"
#include "data/data_session.h"
#include "data/data_channel.h"
#include "info/info_controller.h"
#include "lang/lang_keys.h"
#include "history/history.h"
#include "window/window_peer_menu.h"
#include "ui/widgets/popup_menu.h"
#include "ui/toast/toast.h"
#include "auth_session.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "styles/style_widgets.h"
#include "styles/style_info.h"
#include "styles/style_boxes.h"

namespace Info {
namespace FeedProfile {
namespace {

constexpr auto kChannelsInFeedMin = 4;

} // namespace

class ChannelsController::Row final : public PeerListRow {
public:
	Row(not_null<History*> history);

	QSize actionSize() const override;
	QMargins actionMargins() const override;
	void paintAction(
		Painter &p,
		int x,
		int y,
		int outerWidth,
		bool selected,
		bool actionSelected) override;

	not_null<History*> history() const {
		return _history;
	}

private:
	not_null<History*> _history;

};

ChannelsController::Row::Row(not_null<History*> history)
: PeerListRow(history->peer)
, _history(history) {
}

QSize ChannelsController::Row::actionSize() const {
	return QRect(
			QPoint(),
			st::smallCloseIcon.size()).marginsAdded(
				st::infoFeedLeaveIconMargins).size();
}

QMargins ChannelsController::Row::actionMargins() const {
	return QMargins(
		0,
		(st::infoCommonGroupsList.item.height - actionSize().height()) / 2,
		0,
		0);
}

void ChannelsController::Row::paintAction(
		Painter &p,
		int x,
		int y,
		int outerWidth,
		bool selected,
		bool actionSelected) {
	if (selected) {
		x += st::infoFeedLeaveIconMargins.left();
		y += st::infoFeedLeaveIconMargins.top();
		(actionSelected
			? st::smallCloseIconOver
			: st::smallCloseIcon).paint(p, x, y, outerWidth);
	}
}

ChannelsController::ChannelsController(not_null<Controller*> controller)
: PeerListController()
, _controller(controller)
, _feed(_controller->key().feed()) {
	if (!_feed->channelsLoaded()) {
//		Auth().api().requestFeedChannels(_feed); // #feed
	}
	_controller->setSearchEnabledByContent(false);
}

auto ChannelsController::createRow(not_null<History*> history)
-> std::unique_ptr<Row> {
	auto result = std::make_unique<Row>(history);
	result->setCustomStatus(QString());
	return result;
}

std::unique_ptr<PeerListRow> ChannelsController::createRestoredRow(
		not_null<PeerData*> peer) {
	return createRow(peer->owner().history(peer));
}

void ChannelsController::prepare() {
	setSearchNoResultsText(lang(lng_feed_channels_not_found));
	delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
	delegate()->peerListSetTitle(langFactory(lng_info_feed_channels));

	rebuildRows();
	using Flag = Data::FeedUpdateFlag;
	Auth().data().feedUpdated(
	) | rpl::filter([=](const Data::FeedUpdate &update) {
		return (update.feed == _feed) && (update.flag == Flag::Channels);
	}) | rpl::filter([=] {
		return _feed->channelsLoaded();
	}) | rpl::start_with_next([=] {
		rebuildRows();
	}, lifetime());
}

void ChannelsController::rebuildRows() {
	if (!_feed->channelsLoaded()) {
		return;
	}
	const auto &channels = _feed->channels();
	auto count = delegate()->peerListFullRowsCount();
	for (auto i = 0; i != count;) {
		const auto row = delegate()->peerListRowAt(i);
		const auto peer = row->peer();
		if (ranges::find_if(channels, [=](not_null<History*> history) {
			return (history->peer == peer);
		}) != end(channels)) {
			++i;
		} else {
			delegate()->peerListRemoveRow(row);
			--count;
		}
	}
	for (const auto history : channels) {
		if (auto row = createRow(history)) {
			delegate()->peerListAppendRow(std::move(row));
		}
	}
	delegate()->peerListRefreshRows();
}

std::unique_ptr<PeerListState> ChannelsController::saveState() const {
	auto result = PeerListController::saveState();
	auto my = std::make_unique<SavedState>();
	using Flag = Data::FeedUpdateFlag;

	// Must not capture `this` here, because it dies before my->lifetime.
	Auth().data().feedUpdated(
	) | rpl::filter([feed = _feed](const Data::FeedUpdate &update) {
		return (update.feed == feed) && (update.flag == Flag::Channels);
	}) | rpl::start_with_next([state = result.get()] {
		state->controllerState = nullptr;
	}, my->lifetime);
	result->controllerState = std::move(my);
	return result;
}

void ChannelsController::restoreState(
		std::unique_ptr<PeerListState> state) {
	PeerListController::restoreState(std::move(state));
}

void ChannelsController::rowClicked(not_null<PeerListRow*> row) {
	_controller->parentController()->showPeerHistory(
		row->peer(),
		Window::SectionShow::Way::Forward);
}

void ChannelsController::rowActionClicked(not_null<PeerListRow*> row) {
	Window::DeleteAndLeaveHandler(row->peer())();
}

base::unique_qptr<Ui::PopupMenu> ChannelsController::rowContextMenu(
		QWidget *parent,
		not_null<PeerListRow*> row) {
	auto my = static_cast<Row*>(row.get());
	auto channel = my->history()->peer->asChannel();

	auto result = base::make_unique_q<Ui::PopupMenu>(parent);
	Window::PeerMenuAddMuteAction(channel, [&](
			const QString &text,
			Fn<void()> handler) {
		return result->addAction(text, handler);
	});
	//result->addAction( // #feed
	//	lang(lng_feed_ungroup),
	//	[=] { Window::ToggleChannelGrouping(channel, false); });

	result->addAction(
		lang(lng_profile_leave_channel),
		Window::DeleteAndLeaveHandler(channel));

	return result;
}

void NotificationsController::Start(not_null<Data::Feed*> feed) {
	const auto initBox = [=](not_null<PeerListBox*> box) {
		box->addButton(langFactory(lng_settings_save), [=] {
			const auto count = box->peerListFullRowsCount();
			for (auto i = 0; i != count; ++i) {
				const auto row = box->peerListRowAt(i);
				const auto peer = row->peer();
				const auto muted = !row->checked();
				if (muted != Auth().data().notifyIsMuted(peer)) {
					Auth().data().updateNotifySettings(
						peer,
						(muted
							? Data::NotifySettings::kDefaultMutePeriod
							: 0));
				}
			}
			box->closeBox();
		});
		box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
	};
	Ui::show(Box<PeerListBox>(
		std::make_unique<NotificationsController>(feed),
		initBox));
}

NotificationsController::NotificationsController(
	not_null<Data::Feed*> feed)
: _feed(feed) {
}

void NotificationsController::prepare() {
	setSearchNoResultsText(lang(lng_feed_channels_not_found));
	delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
	delegate()->peerListSetTitle(langFactory(lng_feed_notifications));

	loadMoreRows();
}

void NotificationsController::loadMoreRows() {
	if (_preloadRequestId || _allLoaded) {
		return;
	}
	// const auto hash = 0;
	//_preloadRequestId = request(MTPmessages_GetDialogs( // #feed
	//	MTP_flags(MTPmessages_GetDialogs::Flag::f_feed_id),
	//	MTP_int(_feed->id()),
	//	MTP_int(_preloadOffsetDate),
	//	MTP_int(_preloadOffsetId),
	//	_preloadPeer ? _preloadPeer->input : MTP_inputPeerEmpty(),
	//	MTP_int(Data::Feed::kChannelsLimit),
	//	MTP_int(hash)
	//)).done([=](const MTPmessages_Dialogs &result) {
	//	applyFeedDialogs(result);
	//	_preloadRequestId = 0;
	//}).fail([=](const RPCError &error) {
	//	_preloadRequestId = 0;
	//}).send();
}

void NotificationsController::applyFeedDialogs(
		const MTPmessages_Dialogs &result) {
	const auto [dialogsList, messagesList] = [&] {
		const auto process = [&](const auto &data) {
			_feed->owner().processUsers(data.vusers);
			_feed->owner().processChats(data.vchats);
			return std::make_tuple(&data.vdialogs.v, &data.vmessages.v);
		};
		switch (result.type()) {
		case mtpc_messages_dialogs:
			_allLoaded = true;
			return process(result.c_messages_dialogs());

		case mtpc_messages_dialogsSlice:
			LOG(("API Error: "
				"Unexpected dialogsSlice in feed dialogs list."));
			return process(result.c_messages_dialogsSlice());
		}
		Unexpected("Type in NotificationsController::applyFeedDialogs");
	}();

	App::feedMsgs(*messagesList, NewMessageLast);

	if (dialogsList->empty()) {
		_allLoaded = true;
	}
	auto channels = std::vector<not_null<ChannelData*>>();
	channels.reserve(dialogsList->size());
	for (const auto &dialog : *dialogsList) {
		dialog.match([&](const MTPDdialog &data) {
			if (const auto peerId = peerFromMTP(data.vpeer)) {
				if (peerIsChannel(peerId)) { // #TODO archive
					const auto history = Auth().data().history(peerId);
					const auto channel = history->peer->asChannel();
					history->applyDialog(data);
					channels.emplace_back(channel);
				} else {
					LOG(("API Error: "
						"Unexpected non-channel in folder dialogs list."));
				}
			}
		}, [&](const MTPDdialogFolder &data) {
			LOG(("API Error: Unexpected dialogFolder in folder dialogs list."));
		});
	}
	if (!channels.empty()) {
		auto notMutedChannels = ranges::view::all(
			channels
		) | ranges::view::filter([](not_null<ChannelData*> channel) {
			return !Auth().data().notifyIsMuted(channel);
		});
		delegate()->peerListAddSelectedRows(notMutedChannels);
		for (const auto channel : channels) {
			delegate()->peerListAppendRow(createRow(channel));
		}
	}
	delegate()->peerListRefreshRows();
}

void NotificationsController::rowClicked(not_null<PeerListRow*> row) {
	delegate()->peerListSetRowChecked(row, !row->checked());
}

std::unique_ptr<PeerListRow> NotificationsController::createRow(
		not_null<ChannelData*> channel) {
	return std::make_unique<PeerListRow>(channel);
}

void EditController::Start(
		not_null<Data::Feed*> feed,
		ChannelData *channel) {
	const auto initBox = [=](not_null<PeerListBox*> box) {
		box->addButton(langFactory(lng_settings_save), [=] {
			auto channels = std::vector<not_null<ChannelData*>>();
			const auto main = App::main();
			const auto count = box->peerListFullRowsCount();
			for (auto i = 0; i != count; ++i) {
				const auto row = box->peerListRowAt(i);
				if (row->checked()) {
					channels.push_back(row->peer()->asChannel());
				}
			}
			if (channels.size() < kChannelsInFeedMin) {
				Ui::Toast::Show(lng_feed_select_more_channels(
					lt_count,
					kChannelsInFeedMin));
				return;
			}
			box->closeBox();
			//Auth().api().setFeedChannels(feed, channels); // #feed
		});
		box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
	};
	Ui::show(Box<PeerListBox>(
		std::make_unique<EditController>(feed, channel),
		initBox));
}

EditController::EditController(
	not_null<Data::Feed*> feed,
	ChannelData *channel)
: _feed(feed) {
//, _startWithChannel(channel) { // #feed
}

void EditController::prepare() {
	setSearchNoResultsText(lang(lng_feed_channels_not_found));
	delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
	delegate()->peerListSetTitle(langFactory(
		(_feed->channels().size() < kChannelsInFeedMin
			? lng_feed_create_new
			: lng_feed_edit_title)));

	loadMoreRows();
}

void EditController::loadMoreRows() {
	if (_preloadRequestId || _allLoaded) {
		return;
	}
	//const auto hash = 0; // #feed
	//_preloadRequestId = request(MTPchannels_GetFeedSources(
	//	MTP_flags(0),
	//	MTP_int(0),
	//	MTP_int(hash)
	//)).done([=](const MTPchannels_FeedSources &result) {
	//	applyFeedSources(result);
	//	_preloadRequestId = 0;
	//}).fail([=](const RPCError &error) {
	//	_preloadRequestId = 0;
	//}).send();
}
// #feed
//void EditController::applyFeedSources(
//		const MTPchannels_FeedSources &result) {
//	auto channels = std::vector<not_null<ChannelData*>>();
//
//	switch (result.type()) {
//	case mtpc_channels_feedSourcesNotModified:
//		LOG(("API Error: Unexpected channels.feedSourcesNotModified."));
//		break;
//
//	case mtpc_channels_feedSources: {
//		const auto &data = result.c_channels_feedSources();
//		Auth().api().applyFeedSources(data);
//
//		for (const auto &chat : data.vchats.v) {
//			if (chat.type() == mtpc_channel) {
//				channels.push_back(_feed->owner().channel(chat.c_channel().vid.v));
//			}
//		}
//	} break;
//
//	default: Unexpected("Type in channels.getFeedSources response.");
//	}
//
//	_allLoaded = true;
//	if (channels.size() < kChannelsInFeedMin) {
//		setDescriptionText(lng_feed_too_few_channels(
//			lt_count,
//			kChannelsInFeedMin));
//		delegate()->peerListSetSearchMode(PeerListSearchMode::Disabled);
//	} else {
//		auto alreadyInFeed = ranges::view::all(
//			channels
//		) | ranges::view::filter([&](not_null<ChannelData*> channel) {
//			return (channel->feed() == _feed)
//				|| (channel == _startWithChannel);
//		});
//		delegate()->peerListAddSelectedRows(alreadyInFeed);
//		for (const auto channel : channels) {
//			delegate()->peerListAppendRow(createRow(channel));
//		}
//	}
//	delegate()->peerListRefreshRows();
//}

void EditController::rowClicked(not_null<PeerListRow*> row) {
	delegate()->peerListSetRowChecked(row, !row->checked());
}

std::unique_ptr<PeerListRow> EditController::createRow(
		not_null<ChannelData*> channel) {
	return std::make_unique<PeerListRow>(channel);
}

} // namespace FeedProfile
} // namespace Info
