[GtkTemplate (ui = "/dev/geopjr/Tuba/ui/dialogs/list_edit.ui")]
public class Tuba.Dialogs.ListEdit : Adw.PreferencesDialog {
	~ListEdit () {
		debug ("Destroying ListEdit");
	}

	public enum RepliesPolicy {
		NONE,
		LIST,
		FOLLOWED;

		public string to_string () {
			switch (this) {
				case LIST:
					return "list";
				case FOLLOWED:
					return "followed";
				default:
					return "none";
			}
		}

		public static RepliesPolicy from_string (string? policy) {
			switch (policy) {
				case "list":
					return LIST;
				case "followed":
					return FOLLOWED;
				default:
					return NONE;
			}
		}
	}

	private API.List list { get; set; }
	private Gee.ArrayList<string> memebers_to_be_removed { get; default=new Gee.ArrayList<string> (); }
	public RepliesPolicy replies_policy_active { get; private set; default=RepliesPolicy.NONE; }

	[GtkChild] unowned Adw.EntryRow title_row;
	[GtkChild] unowned Adw.SwitchRow hide_from_home_row;
	[GtkChild] unowned Gtk.CheckButton none_radio;
	[GtkChild] unowned Gtk.CheckButton list_radio;
	[GtkChild] unowned Gtk.CheckButton followed_radio;
	[GtkChild] unowned Adw.PreferencesPage members_page;
	[GtkChild] unowned Adw.PreferencesGroup members_group;

	public string list_title {
		get {
			return title_row.text;
		}
	}

	public bool is_exclusive {
		get {
			return hide_from_home_row.active;
		}
	}

	public ListEdit (API.List t_list) {
		list = t_list;
		title_row.text = t_list.title;
		hide_from_home_row.active = t_list.exclusive;

		update_active_radio_button (RepliesPolicy.from_string (t_list.replies_policy));
		update_members.begin ();
	}

	[GtkCallback]
	private void on_radio_toggled () {
		if (none_radio.active) {
			replies_policy_active = RepliesPolicy.NONE;
			return;
		}

		if (list_radio.active) {
			replies_policy_active = RepliesPolicy.LIST;
			return;
		}

		if (followed_radio.active) {
			replies_policy_active = RepliesPolicy.FOLLOWED;
			return;
		}
	}

	private void update_active_radio_button (RepliesPolicy replies_policy) {
		switch (replies_policy) {
			case RepliesPolicy.LIST:
				list_radio.active = true;
				break;
			case RepliesPolicy.FOLLOWED:
				followed_radio.active = true;
				break;
			default:
				none_radio.active = true;
				break;
		}

		on_radio_toggled ();
	}

	private class MemberCheckButton : Gtk.CheckButton {
		public signal void changed (string member_id, bool active);

		string member_id { get; set; }
		public MemberCheckButton (string member_id) {
			this.member_id = member_id;
			this.css_classes = { "selection-mode", "error" };
			this.tooltip_text = _("Remove");

			this.toggled.connect (on_toggled);
		}

		private void on_toggled () {
			changed (member_id, this.active);
		}
	}

	private async void update_members () {
		var req = new RequestV2 (@"/api/v1/lists/$(list.id)/accounts") { account = accounts.active };

		try {
			var in_stream = yield req.exec (null);
			Json.Parser parser = yield Network.get_parser_from_inputstream_async (in_stream);
			if (Network.get_array_size (parser) > 0) {
				this.add (members_page);

				Network.parse_array (parser, node => {
					var member = API.Account.from (node);
					var avi = new Widgets.Avatar () {
						account = member,
						size = 32
					};

					var remove_button = new MemberCheckButton (member.id) {
						active = false,
						valign = Gtk.Align.CENTER,
						halign = Gtk.Align.CENTER,
					};
					remove_button.changed.connect (on_member_remove_changed);

					var member_row = new Adw.ActionRow () {
						title = member.full_handle
					};

					member_row.add_prefix (avi);
					member_row.add_suffix (remove_button);

					members_group.add (member_row);
				});
			}
		} catch (Error e) {
			// translators: error toast when tuba cant gather the members of a list;
			//				the variable is a string error message
			this.add_toast (new Adw.Toast (_("Couldn't fetch members: %s").printf (e.message)) { timeout = 5 });
			warning (@"Couldn't fetch members: $(e.code) $(e.message)");
		}
	}

	private void on_member_remove_changed (string member_id, bool active) {
		if (active) {
			memebers_to_be_removed.add (member_id);
		} else if (memebers_to_be_removed.contains (member_id)) {
			memebers_to_be_removed.remove (member_id);
		}
	}

	[GtkCallback]
	private void on_close () {
		on_apply.begin ();
		force_close ();
	}

	private async void on_apply () {
		if (list.title != list_title || RepliesPolicy.from_string (list.replies_policy) != replies_policy_active || list.exclusive != is_exclusive) {
			var replies_policy_string = replies_policy_active.to_string ();

			var builder = new Json.Builder ();
			builder.begin_object ();
			builder.set_member_name ("title");
			builder.add_string_value (list_title);
			builder.set_member_name ("replies_policy");
			builder.add_string_value (replies_policy_string);
			builder.set_member_name ("exclusive");
			builder.add_boolean_value (is_exclusive);
			builder.end_object ();

			var req = new RequestV2 (@"/api/v1/lists/$(list.id)", PUT) { account = accounts.active };
			req.set_body_from_json (builder);

			try {
				yield req.exec (null);
				list.title = list_title;
				list.replies_policy = replies_policy_string;
				list.exclusive = is_exclusive;
			} catch (Error e) {
				// translators: error toast when tuba cant save a list;
				//				the variable is a string error message
				app.toast (_("Couldn't save list: %s").printf (e.message));
				warning (@"Couldn't save list: $(e.code) $(e.message)");
			}
		}

		if (memebers_to_be_removed.size > 0) {
			var ids_builder = new Json.Builder ();
			ids_builder.begin_object ();
			ids_builder.set_member_name ("account_ids");
			ids_builder.begin_array ();
			memebers_to_be_removed.foreach (e => {
				ids_builder.add_string_value (e);
				return true;
			});
			ids_builder.end_array ();
			ids_builder.end_object ();

			var req = new RequestV2 (@"/api/v1/lists/$(list.id)/accounts", DELETE) { account = accounts.active };
			req.set_body_from_json (ids_builder);

			try {
				yield req.exec (null);
			} catch (Error e) {
				app.toast (_("Couldn't save list: %s").printf (e.message));
				warning (@"Couldn't save list members: $(e.code) $(e.message)");
			}
		}
	}
}
