public class Tuba.API.PreviewCard : Entity, Widgetizable {
	public enum CardSpecialType {
		BASIC,
		PEERTUBE,
		FUNKWHALE,
		BOOKWYRM,
		CLAPPER;

		public string to_string () {
			switch (this) {
				case PEERTUBE:
					return "PeerTube";
				case FUNKWHALE:
					return "Funkwhale";
				case BOOKWYRM:
					return "BookWyrm";
				default:
					return "";
			}
		}

		//  public string to_dialog_title () {
		//  	switch (this) {
		//  		case PEERTUBE:
		//  			// translators: the variable is an external service like "PeerTube"
		//  			return _("You are about to open a %s video").printf (this.to_string ());
		//  		case FUNKWHALE:
		//  			// translators: the variable is an external service like "Funkwhale",
		//  			//				track as in song
		//  			return _("You are about to open a %s track").printf (this.to_string ());
		//  		case BOOKWYRM:
		//  			// translators: the variable is an external service like "BookWyrm"
		//  			return _("You are about to open a %s book").printf (this.to_string ());
		//  		case CLAPPER:
		//  			return _("You are about to open a video");
		//  		default:
		//  			// translators: the variable is the app name (Tuba)
		//  			return _("You are about to leave %s").printf (Build.NAME);
		//  	}
		//  }

		public string to_dialog_body (string t_url) {
			var dlg_url = t_url;
			if (dlg_url.length > 64) {
				dlg_url = t_url.substring (0, 62) + "…";
			}

			switch (this) {
				case BASIC:
					// translators: the variable is a url
					return _("If you proceed, \"%s\" will open in your browser.").printf (dlg_url);
				default:
					// translators: the first variable is the app name (Tuba),
					//				the second one is a url
					return _("If you proceed, %s will connect to \"%s\".").printf (Build.NAME, dlg_url);
			}
		}
	}

	public class AuthorEntity : Entity {
		public string? name { get; set; }
		public string? url { get; set; }
		public API.Account? account { get; set; }
	}

	public string url { get; set; }
	public string title { get; set; default=""; }
	public string description { get; set; default=""; }
	public string kind { get; set; default="link"; }
	public string author_name { get; set; default=""; }
	public string author_url { get; set; default=""; }
	public Gee.ArrayList<AuthorEntity>? authors { get; set; default=null; }
	public string provider_name { get; set; default=""; }
	public string provider_url { get; set; default=""; }
	public string? image { get; set; default=null; }
	public string? blurhash { get; set; default=null; }
	public Gee.ArrayList<API.TagHistory>? history { get; set; default = null; }

	private GLib.Uri? _tuba_uri = null;
	public GLib.Uri? tuba_uri {
		get {
			if (_tuba_uri == null) {
				try {
					_tuba_uri = GLib.Uri.parse (this.url, GLib.UriFlags.NONE);
				} catch {}
			}

			return _tuba_uri;
		}
	}

	private CardSpecialType? _special_card = null;
	public CardSpecialType special_card {
		get {
			if (_special_card == null) {
				if (is_peertube) {
					#if CLAPPER
						#if CLAPPER_0_10
							if (enhancer_check ("peertube")) {
						#else
							if (Clapper.enhancer_check (typeof (Clapper.Extractable), "peertube", null, null)) {
						#endif
							_special_card = CardSpecialType.PEERTUBE;
						} else {
							_special_card = CardSpecialType.BASIC;
						}
					#else
						_special_card = CardSpecialType.PEERTUBE;
					#endif
				} else if (is_funkwhale) {
					_special_card = CardSpecialType.FUNKWHALE;
				} else if (is_bookwyrm) {
					_special_card = CardSpecialType.BOOKWYRM;
				} else {
					_special_card = CardSpecialType.BASIC;
					#if CLAPPER
						// TODO: maybe limit to https only
						#if CLAPPER_0_10
							if (enhancer_check (this.tuba_uri.get_scheme (), this.tuba_uri.get_host ()))
						#else
							if (Clapper.enhancer_check (typeof (Clapper.Extractable), this.tuba_uri.get_scheme (), this.tuba_uri.get_host (), null))
						#endif
							_special_card = CardSpecialType.CLAPPER;
					#endif
				}
			}

			return _special_card;
		}
	}

	#if CLAPPER_0_10
		private bool enhancer_check (string scheme, string? t_host = null) {
			if (!Clapper.WITH_ENHANCERS_LOADER) return false;
			bool supported = false;

			string? host = t_host;
			if (host != null && host.has_prefix ("www.")) host = host.substring (4);

			var proxies = Clapper.get_global_enhancer_proxies ();
			for (var i = 0; i < proxies.get_n_proxies (); i++) {
				var proxy = proxies.peek_proxy (i);
				if (!proxy.target_has_interface (typeof (Clapper.Extractable))) continue;

				if (!proxy.extra_data_lists_value ("X-Schemes", scheme)) continue;
				// We were only looking for scheme matching
				// e.g. peertube://
				if (host == null) {
					supported = true;
					break;
				}

				if (!proxy.extra_data_lists_value ("X-Hosts", host)) continue;
				// both the host and the scheme
				// match at this point
				supported = true;
				break;
			}

			return supported;
		}
	#endif

	public override Type deserialize_array_type (string prop) {
		switch (prop) {
			case "history":
				return typeof (API.TagHistory);
			case "authors":
				return typeof (AuthorEntity);
		}

		return base.deserialize_array_type (prop);
	}

	private bool is_peertube {
		get {
			#if CLAPPER
				return kind == "video" && provider_name == "PeerTube";
				// #253
			#else
				return false;
			#endif
		}
	}

	private bool is_funkwhale {
		get {
			bool provider_fw = provider_name.last_index_of ("- Funkwhale") > 0;
			bool url_fw = url.last_index_of ("/library/tracks/") > -1;

			return kind == "video" && provider_fw && url_fw;
		}
	}

	private bool is_bookwyrm {
		get {
			return kind == "link" && bookwyrm_regex.match (url);
		}
	}

	public override Gtk.Widget to_widget () {
		if (this.kind == "link" && this.history != null) return new Widgets.PreviewCardExplore (this);

		return new Widgets.PreviewCard (this);
	}

	public void open_special_card () {
		if (this.tuba_uri == null) {
			open_url.begin ();
			return;
		}

		string api_url = "";
		string host = this.tuba_uri.get_host ();
		switch (this.special_card) {
			case FUNKWHALE:
				api_url = @"https://$(host)/api/v1/tracks/$(Path.get_basename (this.tuba_uri.get_path ()))";
				break;
			case BOOKWYRM:
				var bookwyrm_path = this.tuba_uri.get_path ();
				var b_id_start = bookwyrm_path.index_of_char ('/', 1);
				var b_id_end = bookwyrm_path.index_of_char ('/', b_id_start + 1) - 1;
				if (b_id_end <= -1) b_id_end = bookwyrm_path.length - 1;
				var bookwyrm_id = bookwyrm_path.substring (b_id_start + 1, b_id_end - b_id_start);

				api_url = @"https://$(host)/book/$(bookwyrm_id).json";
				break;
			#if CLAPPER
				case API.PreviewCard.CardSpecialType.CLAPPER:
				case API.PreviewCard.CardSpecialType.PEERTUBE:
					string fin_url = this.url;
					if (this.special_card == API.PreviewCard.CardSpecialType.PEERTUBE) {
						fin_url = GLib.Uri.build (
							this.tuba_uri.get_flags (),
							"peertube",
							this.tuba_uri.get_userinfo (),
							host,
							this.tuba_uri.get_port (),
							this.tuba_uri.get_path (),
							this.tuba_uri.get_query (),
							this.tuba_uri.get_fragment ()
						).to_string ();
					}

					app.main_window.show_media_viewer (fin_url, Tuba.Attachment.MediaType.VIDEO, null, null, false, null, fin_url, null, true);
					return;
			#endif
			default:
				open_url.begin ();
				return;
		}

		open_special_card_real.begin (api_url, host);
	}

	private async void open_special_card_real (string api_url, string host) {
		var req = new RequestV2 (api_url);

		try {
			var in_stream = yield req.exec (null);
			Json.Parser parser = yield Network.get_parser_from_inputstream_async (in_stream);
			var node = network.parse_node (parser);

			switch (this.special_card) {
				case API.PreviewCard.CardSpecialType.FUNKWHALE:
					var funkwhale_obj = API.Funkwhale.from (node);
					string res_url;

					if (funkwhale_obj.get_track (host, out res_url)) {
						app.main_window.show_media_viewer (res_url, Tuba.Attachment.MediaType.AUDIO, null, null, false, null, this.url, null, true);
					}
					break;
				case API.PreviewCard.CardSpecialType.BOOKWYRM:
					API.BookWyrm bookwyrm_obj = API.BookWyrm.from (node);

					if (bookwyrm_obj.title != null && bookwyrm_obj.title != "") {
						app.main_window.show_book (bookwyrm_obj);
					}
					break;
				default:
					yield open_url ();
					break;
			}
		} catch (Error e) {
			yield open_url ();
		}
	}

	private async void open_url () {
		if (Widgets.RichLabel.should_resolve (this.tuba_uri, this.url)) {
			try {
				(yield accounts.active.resolve (this.url)).open ();
				return;
			} catch (Error e) {
				warning (@"Failed to resolve URL \"$(this.url)\":");
				warning (e.message);
			}
		}

		#if WEBKIT
			if (settings.use_in_app_browser_if_available && Views.Browser.can_handle (this.tuba_uri, this.url)) {
				(new Views.Browser.with_url (this.url)).present (app.main_window);
				return;
			}
		#endif

		if (this.tuba_uri != null) {
			yield Utils.Host.open_uri (this.tuba_uri);
		} else {
			yield Utils.Host.open_url (this.url);
		}
	}
}
