text: import React from 'react';
  import {
  	View, StyleSheet, Text, Animated, Easing, Image
  } from 'react-native';
  import PropTypes from 'prop-types';
  import { connect } from 'react-redux';
  import { Base64 } from 'js-base64';
  import * as AppleAuthentication from 'expo-apple-authentication';
  
  import { withTheme } from '../theme';
  import sharedStyles from '../views/Styles';
  import { themes } from '../constants/colors';
  import Button from './Button';
  import OrSeparator from './OrSeparator';
  import Touch from '../utils/touch';
  import I18n from '../i18n';
  import random from '../utils/random';
  import RocketChat from '../lib/rocketchat';
  
  const BUTTON_HEIGHT = 48;
  const SERVICE_HEIGHT = 58;
  const BORDER_RADIUS = 2;
  const SERVICES_COLLAPSED_HEIGHT = 174;
  
  const styles = StyleSheet.create({
  	serviceButton: {
  		borderRadius: BORDER_RADIUS,
  		marginBottom: 10
  	},
  	serviceButtonContainer: {
  		borderRadius: BORDER_RADIUS,
  		width: '100%',
  		height: BUTTON_HEIGHT,
  		flexDirection: 'row',
  		alignItems: 'center',
  		justifyContent: 'center',
  		paddingHorizontal: 15
  	},
  	serviceIcon: {
  		position: 'absolute',
  		left: 15,
  		top: 12,
  		width: 24,
  		height: 24
  	},
  	serviceText: {
  		...sharedStyles.textRegular,
  		fontSize: 16
  	},
  	serviceName: {
  		...sharedStyles.textSemibold
  	},
  	options: {
  		marginBottom: 0
  	}
  });
  
  class LoginServices extends React.PureComponent {
  	static propTypes = {
  		navigation: PropTypes.object,
  		server: PropTypes.string,
  		services: PropTypes.object,
  		Gitlab_URL: PropTypes.string,
  		CAS_enabled: PropTypes.bool,
  		CAS_login_url: PropTypes.string,
  		separator: PropTypes.bool,
  		theme: PropTypes.string
  	}
  
  	static defaultProps = {
  		separator: true
  	}
  
  	state = {
  		collapsed: true,
  		servicesHeight: new Animated.Value(SERVICES_COLLAPSED_HEIGHT)
  	}
  
  	onPressFacebook = () => {
  		const { services, server } = this.props;
  		const { clientId } = services.facebook;
  		const endpoint = 'https://m.facebook.com/v2.9/dialog/oauth';
  		const redirect_uri = `${ server }/_oauth/facebook?close`;
  		const scope = 'email';
  		const state = this.getOAuthState();
  		const params = `?client_id=${ clientId }&redirect_uri=${ redirect_uri }&scope=${ scope }&state=${ state }&display=touch`;
  		this.openOAuth({ url: `${ endpoint }${ params }` });
  	}
  
  	onPressGithub = () => {
  		const { services, server } = this.props;
  		const { clientId } = services.github;
  		const endpoint = `https://github.com/login?client_id=${ clientId }&return_to=${ encodeURIComponent('/login/oauth/authorize') }`;
  		const redirect_uri = `${ server }/_oauth/github?close`;
  		const scope = 'user:email';
  		const state = this.getOAuthState();
  		const params = `?client_id=${ clientId }&redirect_uri=${ redirect_uri }&scope=${ scope }&state=${ state }`;
  		this.openOAuth({ url: `${ endpoint }${ encodeURIComponent(params) }` });
  	}
  
  	onPressGitlab = () => {
  		const { services, server, Gitlab_URL } = this.props;
  		const { clientId } = services.gitlab;
  		const baseURL = Gitlab_URL ? Gitlab_URL.trim().replace(/\/*$/, '') : 'https://gitlab.com';
  		const endpoint = `${ baseURL }/oauth/authorize`;
  		const redirect_uri = `${ server }/_oauth/gitlab?close`;
  		const scope = 'read_user';
  		const state = this.getOAuthState();
  		const params = `?client_id=${ clientId }&redirect_uri=${ redirect_uri }&scope=${ scope }&state=${ state }&response_type=code`;
  		this.openOAuth({ url: `${ endpoint }${ params }` });
  	}
  
  	onPressGoogle = () => {
  		const { services, server } = this.props;
  		const { clientId } = services.google;
  		const endpoint = 'https://accounts.google.com/o/oauth2/auth';
  		const redirect_uri = `${ server }/_oauth/google?close`;
  		const scope = 'email';
  		const state = this.getOAuthState();
  		const params = `?client_id=${ clientId }&redirect_uri=${ redirect_uri }&scope=${ scope }&state=${ state }&response_type=code`;
  		this.openOAuth({ url: `${ endpoint }${ params }` });
  	}
  
  	onPressLinkedin = () => {
  		const { services, server } = this.props;
  		const { clientId } = services.linkedin;
  		const endpoint = 'https://www.linkedin.com/oauth/v2/authorization';
  		const redirect_uri = `${ server }/_oauth/linkedin?close`;
  		const scope = 'r_liteprofile,r_emailaddress';
  		const state = this.getOAuthState();
  		const params = `?client_id=${ clientId }&redirect_uri=${ redirect_uri }&scope=${ scope }&state=${ state }&response_type=code`;
  		this.openOAuth({ url: `${ endpoint }${ params }` });
  	}
  
  	onPressMeteor = () => {
  		const { services, server } = this.props;
  		const { clientId } = services['meteor-developer'];
  		const endpoint = 'https://www.meteor.com/oauth2/authorize';
  		const redirect_uri = `${ server }/_oauth/meteor-developer`;
  		const state = this.getOAuthState();
  		const params = `?client_id=${ clientId }&redirect_uri=${ redirect_uri }&state=${ state }&response_type=code`;
  		this.openOAuth({ url: `${ endpoint }${ params }` });
  	}
  
  	onPressTwitter = () => {
  		const { server } = this.props;
  		const state = this.getOAuthState();
  		const url = `${ server }/_oauth/twitter/?requestTokenAndRedirect=true&state=${ state }`;
  		this.openOAuth({ url });
  	}
  
  	onPressWordpress = () => {
  		const { services, server } = this.props;
  		const { clientId, serverURL } = services.wordpress;
  		const endpoint = `${ serverURL }/oauth/authorize`;
  		const redirect_uri = `${ server }/_oauth/wordpress?close`;
  		const scope = 'openid';
  		const state = this.getOAuthState();
  		const params = `?client_id=${ clientId }&redirect_uri=${ redirect_uri }&scope=${ scope }&state=${ state }&response_type=code`;
  		this.openOAuth({ url: `${ endpoint }${ params }` });
  	}
  
  	onPressCustomOAuth = (loginService) => {
  		const { server } = this.props;
  		const {
  			serverURL, authorizePath, clientId, scope, service
  		} = loginService;
  		const redirectUri = `${ server }/_oauth/${ service }`;
  		const state = this.getOAuthState();
  		const params = `?client_id=${ clientId }&redirect_uri=${ redirectUri }&response_type=code&state=${ state }&scope=${ scope }`;
  		const domain = `${ serverURL }`;
  		const absolutePath = `${ authorizePath }${ params }`;
  		const url = absolutePath.includes(domain) ? absolutePath : domain + absolutePath;
  		this.openOAuth({ url });
  	}
  
  	onPressSaml = (loginService) => {
  		const { server } = this.props;
  		const {	clientConfig } = loginService;
  		const {	provider } = clientConfig;
  		const ssoToken = random(17);
  		const url = `${ server }/_saml/authorize/${ provider }/${ ssoToken }`;
  		this.openOAuth({ url, ssoToken, authType: 'saml' });
  	}
  
  	onPressCas = () => {
  		const { server, CAS_login_url } = this.props;
  		const ssoToken = random(17);
  		const url = `${ CAS_login_url }?service=${ server }/_cas/${ ssoToken }`;
  		this.openOAuth({ url, ssoToken, authType: 'cas' });
  	}
  
  	onPressAppleLogin = async() => {
  		try {
  			const { fullName, email, identityToken } = await AppleAuthentication.signInAsync({
  				requestedScopes: [
  					AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
  					AppleAuthentication.AppleAuthenticationScope.EMAIL
  				]
  			});
  
  			await RocketChat.loginOAuthOrSso({ fullName, email, identityToken });
  		} catch {
  			// Do nothing
  		}
  	}
  
  	getOAuthState = () => {
  		const credentialToken = random(43);
  		return Base64.encodeURI(JSON.stringify({ loginStyle: 'popup', credentialToken, isCordova: true }));
  	}
  
  	openOAuth = ({ url, ssoToken, authType = 'oauth' }) => {
  		const { navigation } = this.props;
  		navigation.navigate('AuthenticationWebView', { url, authType, ssoToken });
  	}
  
  	transitionServicesTo = (height) => {
  		const { servicesHeight } = this.state;
  		if (this._animation) {
  			this._animation.stop();
  		}
  		this._animation = Animated.timing(servicesHeight, {
  			toValue: height,
  			duration: 300,
  			easing: Easing.easeOutCubic
  		}).start();
  	}
  
  	toggleServices = () => {
  		const { collapsed } = this.state;
  		const { services } = this.props;
  		const { length } = Object.values(services);
  		if (collapsed) {
  			this.transitionServicesTo(SERVICE_HEIGHT * length);
  		} else {
  			this.transitionServicesTo(SERVICES_COLLAPSED_HEIGHT);
  		}
  		this.setState(prevState => ({ collapsed: !prevState.collapsed }));
  	}
  
  	getSocialOauthProvider = (name) => {
  		const oauthProviders = {
  			facebook: this.onPressFacebook,
  			github: this.onPressGithub,
  			gitlab: this.onPressGitlab,
  			google: this.onPressGoogle,
  			linkedin: this.onPressLinkedin,
  			'meteor-developer': this.onPressMeteor,
  			twitter: this.onPressTwitter,
  			wordpress: this.onPressWordpress
  		};
  		return oauthProviders[name];
  	}
  
  	renderServicesSeparator = () => {
  		const { collapsed } = this.state;
  		const { services, separator, theme } = this.props;
  		const { length } = Object.values(services);
  
  		if (length > 3 && separator) {
  			return (
  				<>
  					<Button
  						title={collapsed ? I18n.t('Onboarding_more_options') : I18n.t('Onboarding_less_options')}
  						type='secondary'
  						onPress={this.toggleServices}
  						theme={theme}
  						style={styles.options}
  						color={themes[theme].actionTintColor}
  					/>
  					<OrSeparator theme={theme} />
  				</>
  			);
  		}
  		if (length > 0 && separator) {
  			return <OrSeparator theme={theme} />;
  		}
  		return null;
  	}
  
  	renderItem = (service) => {
  		const { CAS_enabled, theme } = this.props;
  		let { name } = service;
  		name = name === 'meteor-developer' ? 'meteor' : name;
  		const icon = `icon_${ name }`;
  		const isSaml = service.service === 'saml';
  		let onPress = () => {};
  
  		switch (service.authType) {
  			case 'oauth': {
  				onPress = this.getSocialOauthProvider(service.name);
  				break;
  			}
  			case 'oauth_custom': {
  				onPress = () => this.onPressCustomOAuth(service);
  				break;
  			}
  			case 'saml': {
  				onPress = () => this.onPressSaml(service);
  				break;
  			}
  			case 'cas': {
  				onPress = () => this.onPressCas();
  				break;
  			}
  			case 'apple': {
  				onPress = () => this.onPressAppleLogin();
  				break;
  			}
  			default:
  				break;
  		}
  
  		if (name === 'apple') {
  			return (
  				<AppleAuthentication.AppleAuthenticationButton
  					buttonType={AppleAuthentication.AppleAuthenticationButtonType.CONTINUE}
  					buttonStyle={theme === 'light' ? AppleAuthentication.AppleAuthenticationButtonStyle.BLACK : AppleAuthentication.AppleAuthenticationButtonStyle.WHITE}
  					cornerRadius={BORDER_RADIUS}
  					style={[styles.serviceButton, { height: BUTTON_HEIGHT }]}
  					onPress={onPress}
  				/>
  			);
  		}
  
  		name = name.charAt(0).toUpperCase() + name.slice(1);
  		let buttonText;
  		if (isSaml || (service.service === 'cas' && CAS_enabled)) {
  			buttonText = <Text style={[styles.serviceName, isSaml && { color: service.buttonLabelColor }]}>{name}</Text>;
  		} else {
  			buttonText = (
  				<>
  					{I18n.t('Continue_with')} <Text style={styles.serviceName}>{name}</Text>
  				</>
  			);
  		}
  
  		const backgroundColor = isSaml && service.buttonColor ? service.buttonColor : themes[theme].chatComponentBackground;
  
  		return (
  			<Touch
  				key={service.name}
  				onPress={onPress}
  				style={[styles.serviceButton, { backgroundColor }]}
  				theme={theme}
  				activeOpacity={0.5}
  				underlayColor={themes[theme].buttonText}
  			>
  				<View style={styles.serviceButtonContainer}>
  					{service.authType === 'oauth' ? <Image source={{ uri: icon }} style={styles.serviceIcon} /> : null}
  					<Text style={[styles.serviceText, { color: themes[theme].titleText }]}>{buttonText}</Text>
  				</View>
  			</Touch>
  		);
  	}
  
  	render() {
  		const { servicesHeight } = this.state;
  		const { services, separator } = this.props;
  		const { length } = Object.values(services);
  		const style = {
  			overflow: 'hidden',
  			height: servicesHeight
  		};
  
  		if (length > 3 && separator) {
  			return (
  				<>
  					<Animated.View style={style}>
  						{Object.values(services).map(service => this.renderItem(service))}
  					</Animated.View>
  					{this.renderServicesSeparator()}
  				</>
  			);
  		}
  		return (
  			<>
  				{Object.values(services).map(service => this.renderItem(service))}
  				{this.renderServicesSeparator()}
  			</>
  		);
  	}
  }
  
  const mapStateToProps = state => ({
  	server: state.server.server,
  	Gitlab_URL: state.settings.API_Gitlab_URL,
  	CAS_enabled: state.settings.CAS_enabled,
  	CAS_login_url: state.settings.CAS_login_url,
  	services: state.login.services
  });
  
  export default connect(mapStateToProps)(withTheme(LoginServices));
  