use conduit::{debug, info, trace, warn, Result};
use ruma::{events::room::message::RoomMessageEventContent, EventId, Mxc, MxcUri, ServerName};

use crate::{admin_command, utils::parse_local_user_id};

#[admin_command]
pub(super) async fn delete(
	&self, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
) -> Result<RoomMessageEventContent> {
	if event_id.is_some() && mxc.is_some() {
		return Ok(RoomMessageEventContent::text_plain(
			"Please specify either an MXC or an event ID, not both.",
		));
	}

	if let Some(mxc) = mxc {
		debug!("Got MXC URL: {mxc}");
		self.services
			.media
			.delete(&mxc.as_str().try_into()?)
			.await?;

		return Ok(RoomMessageEventContent::text_plain(
			"Deleted the MXC from our database and on our filesystem.",
		));
	} else if let Some(event_id) = event_id {
		debug!("Got event ID to delete media from: {event_id}");

		let mut mxc_urls = vec![];
		let mut mxc_deletion_count: usize = 0;

		// parsing the PDU for any MXC URLs begins here
		if let Some(event_json) = self.services.rooms.timeline.get_pdu_json(&event_id)? {
			if let Some(content_key) = event_json.get("content") {
				debug!("Event ID has \"content\".");
				let content_obj = content_key.as_object();

				if let Some(content) = content_obj {
					// 1. attempts to parse the "url" key
					debug!("Attempting to go into \"url\" key for main media file");
					if let Some(url) = content.get("url") {
						debug!("Got a URL in the event ID {event_id}: {url}");

						if url.to_string().starts_with("\"mxc://") {
							debug!("Pushing URL {url} to list of MXCs to delete");
							let final_url = url.to_string().replace('"', "");
							mxc_urls.push(final_url);
						} else {
							info!("Found a URL in the event ID {event_id} but did not start with mxc://, ignoring");
						}
					}

					// 2. attempts to parse the "info" key
					debug!("Attempting to go into \"info\" key for thumbnails");
					if let Some(info_key) = content.get("info") {
						debug!("Event ID has \"info\".");
						let info_obj = info_key.as_object();

						if let Some(info) = info_obj {
							if let Some(thumbnail_url) = info.get("thumbnail_url") {
								debug!("Found a thumbnail_url in info key: {thumbnail_url}");

								if thumbnail_url.to_string().starts_with("\"mxc://") {
									debug!("Pushing thumbnail URL {thumbnail_url} to list of MXCs to delete");
									let final_thumbnail_url = thumbnail_url.to_string().replace('"', "");
									mxc_urls.push(final_thumbnail_url);
								} else {
									info!(
										"Found a thumbnail URL in the event ID {event_id} but did not start with \
										 mxc://, ignoring"
									);
								}
							} else {
								info!("No \"thumbnail_url\" key in \"info\" key, assuming no thumbnails.");
							}
						}
					}

					// 3. attempts to parse the "file" key
					debug!("Attempting to go into \"file\" key");
					if let Some(file_key) = content.get("file") {
						debug!("Event ID has \"file\".");
						let file_obj = file_key.as_object();

						if let Some(file) = file_obj {
							if let Some(url) = file.get("url") {
								debug!("Found url in file key: {url}");

								if url.to_string().starts_with("\"mxc://") {
									debug!("Pushing URL {url} to list of MXCs to delete");
									let final_url = url.to_string().replace('"', "");
									mxc_urls.push(final_url);
								} else {
									info!(
										"Found a URL in the event ID {event_id} but did not start with mxc://, \
										 ignoring"
									);
								}
							} else {
								info!("No \"url\" key in \"file\" key.");
							}
						}
					}
				} else {
					return Ok(RoomMessageEventContent::text_plain(
						"Event ID does not have a \"content\" key or failed parsing the event ID JSON.",
					));
				}
			} else {
				return Ok(RoomMessageEventContent::text_plain(
					"Event ID does not have a \"content\" key, this is not a message or an event type that contains \
					 media.",
				));
			}
		} else {
			return Ok(RoomMessageEventContent::text_plain(
				"Event ID does not exist or is not known to us.",
			));
		}

		if mxc_urls.is_empty() {
			// we shouldn't get here (should have errored earlier) but just in case for
			// whatever reason we do...
			info!("Parsed event ID {event_id} but did not contain any MXC URLs.");
			return Ok(RoomMessageEventContent::text_plain("Parsed event ID but found no MXC URLs."));
		}

		for mxc_url in mxc_urls {
			self.services
				.media
				.delete(&mxc_url.as_str().try_into()?)
				.await?;
			mxc_deletion_count = mxc_deletion_count.saturating_add(1);
		}

		return Ok(RoomMessageEventContent::text_plain(format!(
			"Deleted {mxc_deletion_count} total MXCs from our database and the filesystem from event ID {event_id}."
		)));
	}

	Ok(RoomMessageEventContent::text_plain(
		"Please specify either an MXC using --mxc or an event ID using --event-id of the message containing an image. \
		 See --help for details.",
	))
}

#[admin_command]
pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
	if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
	{
		return Ok(RoomMessageEventContent::text_plain(
			"Expected code block in command body. Add --help for details.",
		));
	}

	let mxc_list = self
		.body
		.to_vec()
		.drain(1..self.body.len().checked_sub(1).unwrap())
		.collect::<Vec<_>>();

	let mut mxc_deletion_count: usize = 0;

	for mxc in mxc_list {
		debug!("Deleting MXC {mxc} in bulk");
		self.services.media.delete(&mxc.try_into()?).await?;
		mxc_deletion_count = mxc_deletion_count
			.checked_add(1)
			.expect("mxc_deletion_count should not get this high");
	}

	Ok(RoomMessageEventContent::text_plain(format!(
		"Finished bulk MXC deletion, deleted {mxc_deletion_count} total MXCs from our database and the filesystem.",
	)))
}

#[admin_command]
pub(super) async fn delete_past_remote_media(&self, duration: String, force: bool) -> Result<RoomMessageEventContent> {
	let deleted_count = self
		.services
		.media
		.delete_all_remote_media_at_after_time(duration, force)
		.await?;

	Ok(RoomMessageEventContent::text_plain(format!(
		"Deleted {deleted_count} total files.",
	)))
}

#[admin_command]
pub(super) async fn delete_all_from_user(&self, username: String, force: bool) -> Result<RoomMessageEventContent> {
	let user_id = parse_local_user_id(self.services, &username)?;

	let deleted_count = self
		.services
		.media
		.delete_from_user(&user_id, force)
		.await?;

	Ok(RoomMessageEventContent::text_plain(format!(
		"Deleted {deleted_count} total files.",
	)))
}

#[admin_command]
pub(super) async fn delete_all_from_server(
	&self, server_name: Box<ServerName>, force: bool,
) -> Result<RoomMessageEventContent> {
	if server_name == self.services.globals.server_name() {
		return Ok(RoomMessageEventContent::text_plain("This command only works for remote media."));
	}

	let Ok(all_mxcs) = self.services.media.get_all_mxcs().await else {
		return Ok(RoomMessageEventContent::text_plain("Failed to get MXC URIs from our database"));
	};

	let mut deleted_count: usize = 0;

	for mxc in all_mxcs {
		let mxc_server_name = match mxc.server_name() {
			Ok(server_name) => server_name,
			Err(e) => {
				if force {
					warn!("Failed to parse MXC {mxc} server name from database, ignoring error and skipping: {e}");
					continue;
				}

				return Ok(RoomMessageEventContent::text_plain(format!(
					"Failed to parse MXC {mxc} server name from database: {e}",
				)));
			},
		};

		if mxc_server_name != server_name || self.services.globals.server_is_ours(mxc_server_name) {
			trace!("skipping MXC URI {mxc}");
			continue;
		}

		let mxc: Mxc<'_> = mxc.as_str().try_into()?;

		match self.services.media.delete(&mxc).await {
			Ok(()) => {
				deleted_count = deleted_count.saturating_add(1);
			},
			Err(e) => {
				if force {
					warn!("Failed to delete {mxc}, ignoring error and skipping: {e}");
					continue;
				}

				return Ok(RoomMessageEventContent::text_plain(format!("Failed to delete MXC {mxc}: {e}")));
			},
		}
	}

	Ok(RoomMessageEventContent::text_plain(format!(
		"Deleted {deleted_count} total files.",
	)))
}