import React, {useContext, useState} from "react";
import styles from "../item/Message.module.css";
import stylesAttachment from "./MessageAttachmentDownloadable.module.css";

import {mimeTypeToPreview} from "../../../../util/conversationUtils";
import {ButtonBase, CircularProgress} from "@mui/material";
import {GetAppRounded} from "@mui/icons-material";
import {formatFileSize} from "../../../../util/fileUtils";
import * as ConnectionManager from "../../../../connection/connectionManager";
import {DecorativeMessageBubble, MessagePartProps} from "../item/Message";
import {StickerItem, TapbackItem} from "../../../../data/blocks";
import {SnackbarContext} from "../../../control/SnackbarProvider";
import {AttachmentRequestErrorCode} from "../../../../data/stateCodes";
import FileDownloadResult from "shared/data/fileDownloadResult";

export default function MessageAttachmentDownloadable(props: {
	data?: ArrayBuffer | Blob,
	name: string | undefined,
	type: string,
	size: number,
	guid: string,
	onDataAvailable: (result: FileDownloadResult) => void,
	onDataClicked: (data: ArrayBuffer | Blob) => void,
	partProps: MessagePartProps,
	tapbacks?: TapbackItem[],
	stickers?: StickerItem[]}
) {
	//State
	const [isDownloading, setIsDownloading] = useState(false);
	const [sizeAvailable, setSizeAvailable] = useState(props.size);
	const [sizeDownloaded, setSizeDownloaded] = useState<number | undefined>(undefined);
	
	const displaySnackbar = useContext(SnackbarContext);
	
	//Display the file name if it is available, otherwise just display the file type
	const nameDisplay = props.name ?? mimeTypeToPreview(props.type);
	
	function startDownload() {
		//Checking if data is already available
		if(props.data) {
			props.onDataClicked(props.data);
			return;
		}
		
		//Setting the state as downloading
		setIsDownloading(true);
		
		//Sending the request and setting the state to downloading
		ConnectionManager.fetchAttachment(props.guid)
			.progress((progress) => {
				if(progress.type === "size") {
					setSizeAvailable(progress.value);
				} else {
					setSizeDownloaded(progress.value);
				}
			})
			.then((fileData) => {
				//Calling the listener
				props.onDataAvailable(fileData);
				
				//Resetting the state
				setIsDownloading(false);
				setSizeDownloaded(undefined);
			})
			.catch((error: AttachmentRequestErrorCode) => {
				//Resetting the state
				setIsDownloading(false);
				setSizeDownloaded(undefined);
				
				//Notifying the user with a snackbar
				displaySnackbar({message: "Failed to download attachment: " + errorCodeToMessage(error)});
			});
	}
	
	return (
		<DecorativeMessageBubble element={ButtonBase} className={`${styles.textBubble} ${stylesAttachment.root}`} style={props.partProps} disabled={isDownloading} onClick={startDownload} tapbacks={props.tapbacks} stickers={props.stickers}>
			<div className={stylesAttachment.icon}>
				{
					isDownloading ?
						<CircularProgress size={24} variant={sizeDownloaded === undefined ? "indeterminate" : "determinate"} value={(sizeDownloaded ?? 0) / sizeAvailable * 100} style={{color: props.partProps.color}} /> :
						<GetAppRounded />
				}
			</div>
			<div className={stylesAttachment.description}>
				<span>{nameDisplay}</span>
				<br />
				<span className={stylesAttachment.descriptionSecondary}>{
					isDownloading ?
						formatFileSize(sizeDownloaded ?? 0) + " of " + formatFileSize(sizeAvailable):
						formatFileSize(sizeAvailable) + " • Click to download"}
				</span>
			</div>
		</DecorativeMessageBubble>
	);
}

function errorCodeToMessage(error: AttachmentRequestErrorCode): string {
	switch(error) {
		case AttachmentRequestErrorCode.Timeout:
			return "Request timed out";
		case AttachmentRequestErrorCode.BadResponse:
			return "A communication error occurred";
		case AttachmentRequestErrorCode.ServerUnknown:
			return "An unknown external error occurred";
		case AttachmentRequestErrorCode.ServerNotFound:
			return "Message not found";
		case AttachmentRequestErrorCode.ServerNotSaved:
			return "Attachment file not found";
		case AttachmentRequestErrorCode.ServerUnreadable:
			return "No permission to read file";
		case AttachmentRequestErrorCode.ServerIO:
			return "Failed to read file";
	}
}