import React, { Component } from "react";
import { View, StyleSheet, Dimensions } from "react-native";
import YoutubePlayer, { getYoutubeMeta } from "react-native-youtube-iframe";
import { Vimeo } from "react-native-vimeo-iframe";
import { WebView } from "react-native-webview";
import { Video, ResizeMode } from "expo-av";
import { Spinner } from "./Spinner";

const SCREEN_HEIGHT = Dimensions.get("window").height;
const SCREEN_WIDTH = Dimensions.get("window").width;
const EXPO_VIDEO_PROPS = { rate: 1, isMuted: false, volume: 1 };
const RETRY_RECOVER = 30000;
const RETRY_LOADING = 10000;

class MediaPlayer extends Component {
	constructor(props) {
		super(props);
		this.state = {
			deviceOrientation: null,
			isLandscape: false,
			isUrlLink: true,
			isYoutube: false,
			isVimeo: false,
			forceWebview: false,
			heightFactor: 0,
			playbackLoaded: false,
			playbackBuffering: false,
			playbackPlaying: false,
			readyToRender: false,
		};
		this.youtubePlayer = null;
		this.videoPlayer = null;
		this.checkStreamLoaded = null;
		this.retryLoading = false;
		this.orientationQueue = [];
	}

	componentDidMount = async () => {
		await this.setupForPlaying();
	};

	componentWillUnmount = () => {
		if (this.checkStreamLoaded) clearInterval(this.checkStreamLoaded);
	};

	getYoutubeVideoId = (url) => {
		url = url
			.replace(/(>|<)/gi, "")
			.split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/);
		if (url[2] !== undefined) {
			const ids = url[2].split(/[^0-9a-z_\-]/i);
			return ids[0];
		} else {
			return "";
		}
	};

	getVimeoId = (url) => {
		const result = url.match(
			/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|video\/|)(\d+)(?:[a-zA-Z0-9_\-]+)?/i,
		);
		return result[1];
	};

	setupForPlaying = async () => {
		// Extract live stream info
		const { source, useVideoPlayer } = this.props;
		const isUrlLink = source && source.startsWith("http");
		const isYoutube =
			source &&
			(source.includes("youtube.com/watch?v=") || source.includes("youtu.be/"));
		const isVimeo = source && source.includes("vimeo.com/");
		let heightFactor = 0;

		if (isYoutube) {
			const metadata = await getYoutubeMeta(this.getYoutubeVideoId(source));
			heightFactor = metadata.height / metadata.width;
		} else if (useVideoPlayer) {
			heightFactor = 0;
		} else {
			heightFactor = 0.565;
		}

		this.setState({
			isUrlLink,
			isYoutube,
			isVimeo,
			playbackLoaded: isYoutube || isVimeo,
			heightFactor,
			readyToRender: true,
		});
	};

	getStreamPlaybackUrl = () => {
		// return 'https://stream.mux.com/Ym4nKktRpGuZNBBBlzhxpyRwZLpvMlPRchcu01x6Ur600.m3u8';
		return `https://stream.mux.com/${this.props.playbackId}.m3u8`;
	};

	checkReloadStream = (wait = RETRY_LOADING, force = false) => {
		if (!force && this.retryLoading) return;

		console.log(`Check loading in ${wait} milliseconds...`);
		this.retryLoading = true;
		setTimeout(async () => {
			const { playbackLoaded, playbackBuffering } = this.state;
			try {
				if (playbackLoaded === false || playbackBuffering === true) {
					console.log("Reloading started");
					await this.videoPlayer.loadAsync(
						{
							uri: this.getStreamPlaybackUrl(),
							overrideFileExtensionAndroid: "m3u8",
						},
						EXPO_VIDEO_PROPS,
						false,
					);
				} else {
					console.log("Already loaded - not reloading");
				}
				this.retryLoading = false;
			} catch {}
		}, wait);
	};

	isLandscape = (deviceRotation) => {
		const { beta, gamma } = deviceRotation;
		const absGamma = Math.abs(gamma);
		const absBeta = Math.abs(beta);
		const isGammaNegative = Math.sign(gamma) === -1;

		if (absGamma <= 0.04 && absBeta <= 0.24) {
			//Portrait mode, on a flat surface.
			return false;
		} else if ((absGamma <= 1.0 || absGamma >= 2.3) && absBeta >= 0.5) {
			//General Portrait mode, accounting for forward and back tilt on the top of the phone.
			return false;
		} else {
			if (isGammaNegative) {
				//Landscape mode with the top of the phone to the left.
				return true;
			} else {
				//Landscape mode with the top of the phone to the right.
				return true;
			}
		}
	};

	onMotionChange = async (changeEvent) => {
		const deviceRotation = changeEvent.rotation;
		const deviceOrientation = changeEvent.orientation;
		const isLandscape = this.isLandscape(deviceRotation);
		this.orientationQueue.push(isLandscape);
		if (this.orientationQueue.length > 2)
			this.orientationQueue = this.orientationQueue.slice(-2);
		// console.log('onMotionChange - orientationQueue', this.orientationQueue);
		if (
			isLandscape !== this.state.isLandscape &&
			isLandscape === (Math.abs(deviceOrientation) === 90) &&
			this.orientationQueue.every((i) => i === isLandscape)
		) {
			this.setState({ isLandscape }, () => {
				// console.log('isLandscape', this.state.isLandscape);
				if (this.props.orientationChanged)
					this.props.orientationChanged(this.state.isLandscape);
			});
		}
	};

	onStreamLoadStart = () => {
		console.log("Stream load started");
		this.checkStreamLoaded = setInterval(() => {
			const { playbackLoaded } = this.state;
			if (playbackLoaded === false) {
				console.log("Stream loading failed unexpectedly");
				this.checkReloadStream(1000, true);
			} else {
				clearInterval(this.checkStreamLoaded);
			}
		}, RETRY_RECOVER);
	};

	onStreamLoaded = (status) => {
		console.log(
			`Stream loaded - isLoaded:${status.isLoaded}, isBuffering:${status.isBuffering}, isPlaying:${status.isPlaying}`,
		);
	};

	onStreamError = (error) => {
		console.log("Stream error", error);
	};

	onStreamStatusUpdate = (status) => {
		this.setState(
			{
				playbackLoaded: status.isLoaded,
				playbackBuffering: status.isBuffering,
				playbackPlaying: status.isPlaying,
			},
			() => {
				const {
					playbackLoaded,
					playbackBuffering,
					playbackPlaying,
					shouldPlay,
				} = this.state;
				// console.log(`Status updated - isLoaded:${playbackLoaded}, isBuffering:${playbackBuffering}, isPlaying:${playbackPlaying}`);
				if (
					playbackLoaded === false &&
					playbackBuffering === undefined &&
					playbackPlaying === undefined
				) {
					this.checkReloadStream();
				} else if (playbackLoaded === true && playbackBuffering === true) {
					this.checkReloadStream(RETRY_RECOVER);
				}
			},
		);
	};

	onYoutubeError = (e) => {
		// console.log('onYoutubeError', e);
		this.setState({ forceWebview: true });
	};

	onYoutubeReady = () => {
		// console.log('onYoutubeReady');
	};

	onVideoPlayStatusUpdate = (status) => {
		// console.log('onVideoPlayStatusUpdate', status);
	};

	onVideoPlayError = (error) => {
		if (error && error.type === "NonFatal") {
			return;
		}
		// console.log('onVideoPlayError', error);
		this.setState({ forceWebview: true });
	};

	getYoutubePlayer = (youtubeId, width, height, autoPlay) => (
		<YoutubePlayer
			ref={(ref) => (this.youtubePlayer = ref)}
			height={height}
			width={width}
			videoId={youtubeId}
			play={autoPlay}
			// onChangeState={event => console.log('onChangeState', event)}
			onReady={this.onYoutubeReady}
			onError={this.onYoutubeError}
			// onPlaybackQualityChange={q => console.log('onPlaybackQualityChange', q)}
			volume={50}
			playbackRate={1}
			playerParams={{
				cc_lang_pref: "us",
				showClosedCaptions: true,
			}}
		/>
	);

	getVimeoPlayer = (vimeoId, width, height, autoPlay) => (
		<View style={{ width, height }}>
			<Vimeo
				style={{ marginLeft: -8, marginRight: -5 }}
				videoId={vimeoId}
				// onReady={() => console.log('Video is ready')}
				// onPlay={() => console.log('Video is playing')}
				// onPlayProgress={data => console.log('Video progress data:', data)}
				// onFinish={() => console.log('Video is finished')}
				loop={false}
				autoPlay={autoPlay}
				controls={true}
				speed={true}
				time={"0m0s"}
			/>
		</View>
	);

	getWebviewPlayer = (embedUrl, width, height) => (
		<WebView
			startInLoadingState
			javaScriptEnabled
			scrollEnabled={false}
			automaticallyAdjustContentInsets={false}
			mediaPlaybackRequiresUserAction
			style={[styles.webView, { width, height }]}
			source={{ uri: embedUrl }}
		/>
	);

	getStreamPlayer = (embedUrl, width, height, autoPlay) => (
		<Video
			ref={(vp) => {
				this.videoPlayer = vp;
			}}
			source={{ uri: embedUrl }}
			{...EXPO_VIDEO_PROPS}
			shouldPlay={autoPlay}
			resizeMode={ResizeMode.CONTAIN}
			useNativeControls
			style={{ width, height }}
			onLoadStart={this.onStreamLoadStart}
			onLoad={this.onStreamLoaded}
			onError={this.onStreamError}
			onPlaybackStatusUpdate={this.onStreamStatusUpdate}
		/>
	);

	// getVideoPlayer = (embedUrl, width, height, autoPlay) => (
	//   <VideoPlayer
	//     videoProps={{
	//       shouldPlay: autoPlay,
	//       resizeMode: Video.RESIZE_MODE_CONTAIN,
	//       source: { uri: embedUrl },
	//     }}
	//     showFullscreenButton={false}
	//     errorCallback={this.onVideoPlayError}
	//     playbackCallback={this.onVideoPlayStatusUpdate}
	//     height={height}
	//     width={width}
	//   />
	// );

	render() {
		let width, height;
		const { playbackId, source, autoPlay, useVideoPlayer } = this.props;
		const {
			readyToRender,
			isLandscape,
			heightFactor,
			isUrlLink,
			isYoutube,
			isVimeo,
			forceWebview,
			playbackLoaded,
			playbackBuffering,
		} = this.state;
		if (!readyToRender) return null;

		if (isLandscape) {
			width = SCREEN_HEIGHT;
			height = SCREEN_WIDTH;
		} else {
			width = SCREEN_WIDTH;
			height = heightFactor ? SCREEN_WIDTH * heightFactor : SCREEN_HEIGHT;
		}

		let embedUrl, player, showLoading;
		if (!forceWebview) {
			if (isYoutube) {
				const youtubeId = this.getYoutubeVideoId(source);
				embedUrl = youtubeId
					? `https://www.youtube.com/embed/${youtubeId}`
					: source;
				// console.log('getYoutubePlayer', youtubeId, width, height, autoPlay);
				player = this.getYoutubePlayer(youtubeId, width, height, autoPlay);
			} else if (isVimeo) {
				const vimeoId = this.getVimeoId(source);
				embedUrl = source;
				// console.log('getVimeoPlayer', vimeoId, width, height, autoPlay);
				player = this.getVimeoPlayer(vimeoId, width, height, autoPlay);
			} else if (playbackId) {
				embedUrl = this.getStreamPlaybackUrl();
				// console.log('getStreamPlayer', embedUrl, width, height, autoPlay);
				player = this.getStreamPlayer(embedUrl, width, height, autoPlay);
				showLoading = true;
			} else if (useVideoPlayer) {
				embedUrl = source;
				// console.log('getVideoPlayer', embedUrl, width, height, autoPlay);
				player = this.getStreamPlayer(embedUrl, width, height, autoPlay);
			}
		}
		if (!player && isUrlLink) {
			embedUrl = source;
			console.log("getWebviewPlayer", embedUrl, width, height);
			player = this.getWebviewPlayer(embedUrl, width, height);
		}

		// console.log({
		//   Streaming: embedUrl,
		//   Landscape: isLandscape,
		//   Loaded: playbackLoaded,
		//   Buffering: playbackBuffering,
		//   LoadingIndicator: showLoading,
		//   Width: width,
		//   Height: height,
		//   HeightFactor: heightFactor,
		//   isYoutube,
		//   isVimeo,
		//   playbackId,
		//   useVideoPlayer,
		// });
		return (
			<View style={styles.container}>
				{player}
				{showLoading && (!playbackLoaded || playbackBuffering) && (
					<View style={styles.loadingContainer}>
						<Spinner color={"#fff"} />
					</View>
				)}
			</View>
		);
	}
}

const styles = StyleSheet.create({
	container: {
		flex: 1,
		backgroundColor: "#000",
		alignItems: "center",
		justifyContent: "center",
	},
	webView: {
		flex: 1,
	},
	loadingContainer: {
		position: "absolute",
		top: 50,
		left: 0,
		right: 0,
		bottom: 50,
		justifyContent: "center",
		alignItems: "center",
	},
});

export default MediaPlayer;
