import { merge } from 'lodash';
import { combineReducers } from 'redux';
import {
	JETPACK_CONNECTION_STATUS_FETCH,
	JETPACK_SET_INITIAL_STATE,
	CONNECT_URL_FETCH,
	CONNECT_URL_FETCH_FAIL,
	CONNECT_URL_FETCH_SUCCESS,
	USER_CONNECTION_DATA_FETCH,
	USER_CONNECTION_DATA_FETCH_FAIL,
	USER_CONNECTION_DATA_FETCH_SUCCESS,
	DISCONNECT_SITE,
	DISCONNECT_SITE_FAIL,
	DISCONNECT_SITE_SUCCESS,
	CONNECT_USER,
	RESET_CONNECT_USER,
	UNLINK_USER,
	UNLINK_USER_FAIL,
	UNLINK_USER_SUCCESS,
	MOCK_SWITCH_USER_PERMISSIONS,
	SITE_RECONNECT,
	SITE_RECONNECT_FAIL,
	SITE_RECONNECT_SUCCESS,
	JETPACK_CONNECTION_HAS_SEEN_WC_CONNECTION_MODAL,
} from 'state/action-types';
import {
	getModulesThatRequireConnection,
	getModulesThatRequireUserConnection,
} from 'state/modules';

export const status = (
	state = { siteConnected: window.Initial_State.connectionStatus },
	action
) => {
	switch ( action.type ) {
		case JETPACK_CONNECTION_STATUS_FETCH:
			return Object.assign( {}, state, { siteConnected: action.siteConnected } );
		case DISCONNECT_SITE_SUCCESS:
			return Object.assign( {}, state, { siteConnected: action.siteConnected } );
		case UNLINK_USER_SUCCESS:
			return Object.assign( {}, state, {
				siteConnected: { ...state.siteConnected, isUserConnected: false },
			} );
		case USER_CONNECTION_DATA_FETCH_SUCCESS:
			if ( true === action.userConnectionData?.currentUser?.isConnected ) {
				return Object.assign( {}, state, {
					siteConnected: {
						...state.siteConnected,
						hasConnectedOwner: true,
					},
				} );
			}

			return state;
		default:
			return state;
	}
};

export const connectUrl = ( state = '', action ) => {
	switch ( action.type ) {
		case JETPACK_SET_INITIAL_STATE:
			return action?.initialState?.connectUrl ?? state;
		case CONNECT_URL_FETCH_SUCCESS:
			return action.connectUrl;
		default:
			return state;
	}
};

export const user = ( state = window.Initial_State.userData || {}, action ) => {
	switch ( action.type ) {
		case USER_CONNECTION_DATA_FETCH_SUCCESS:
			return Object.assign( {}, state, action.userConnectionData );

		case UNLINK_USER_SUCCESS: {
			const currentUser = Object.assign( {}, state.currentUser, { isConnected: false } );
			return Object.assign( {}, state, { currentUser } );
		}

		case MOCK_SWITCH_USER_PERMISSIONS:
			return merge( {}, state, action.initialState );

		default:
			return state;
	}
};

export const connectionRequests = {
	disconnectingSite: false,
	connectingUser: false,
	unlinkingUser: false,
	fetchingConnectUrl: false,
	fetchingUserData: false,
	reconnectingSite: false,
};

export const requests = ( state = connectionRequests, action ) => {
	switch ( action.type ) {
		case DISCONNECT_SITE:
			return Object.assign( {}, state, { disconnectingSite: true } );
		case UNLINK_USER:
			return Object.assign( {}, state, { unlinkingUser: true } );
		case CONNECT_USER:
			return Object.assign( {}, state, {
				connectingUser: true,
				connectingUserFeatureLabel: action.featureLabel,
				connectingUserFrom: action.from,
			} );
		case RESET_CONNECT_USER:
			return Object.assign( {}, state, { connectingUser: false } );
		case CONNECT_URL_FETCH:
			return Object.assign( {}, state, { fetchingConnectUrl: true } );
		case USER_CONNECTION_DATA_FETCH:
			return Object.assign( {}, state, { fetchingUserData: true } );
		case SITE_RECONNECT:
			return Object.assign( {}, state, { reconnectingSite: true } );

		case DISCONNECT_SITE_FAIL:
		case DISCONNECT_SITE_SUCCESS:
			return Object.assign( {}, state, { disconnectingSite: false } );

		case UNLINK_USER_FAIL:
		case UNLINK_USER_SUCCESS:
			return Object.assign( {}, state, { unlinkingUser: false } );

		case CONNECT_URL_FETCH_FAIL:
		case CONNECT_URL_FETCH_SUCCESS:
			return Object.assign( {}, state, { fetchingConnectUrl: false } );
		case USER_CONNECTION_DATA_FETCH_FAIL:
		case USER_CONNECTION_DATA_FETCH_SUCCESS:
			return Object.assign( {}, state, { fetchingUserData: false } );

		case SITE_RECONNECT_FAIL:
		case SITE_RECONNECT_SUCCESS:
			return Object.assign( {}, state, { reconnectingSite: false } );

		default:
			return state;
	}
};

export const hasSeenWCConnectionModal = (
	state = window.Initial_State.hasSeenWCConnectionModal || false,
	action
) => {
	switch ( action.type ) {
		case JETPACK_CONNECTION_HAS_SEEN_WC_CONNECTION_MODAL:
			return true;

		default:
			return state;
	}
};

export const reducer = combineReducers( {
	connectUrl,
	status,
	user,
	requests,
	hasSeenWCConnectionModal,
} );

/**
 * Get the whole connection status object.
 *
 * @param {object} state - Global state tree
 * @return {object} Connection status object.
 */
export function getConnectionStatus( state ) {
	return 'object' === typeof state.jetpack.connection.status.siteConnected
		? state.jetpack.connection.status.siteConnected
		: false;
}

/**
 * Returns true if site is connected to WordPress.com
 *
 * @param {object} state - Global state tree
 * @return {boolean|string} True if site is connected, False if it is not, 'offline' if site is in offline mode.
 */
export function getSiteConnectionStatus( state ) {
	if ( 'object' !== typeof state.jetpack.connection.status.siteConnected ) {
		return false;
	}
	if ( state.jetpack.connection.status.siteConnected.offlineMode.isActive ) {
		return 'offline';
	}
	return state.jetpack.connection.status.siteConnected.isActive;
}

/**
 * Checks if the site is connected to WordPress.com. Unlike getSiteConnectionStatus, this one returns only a boolean.
 *
 * @param {object} state - Global state tree
 * @return {boolean} True if site is connected to WordPress.com. False if site is in Offline Mode or there's no connection data.
 */
export function isSiteConnected( state ) {
	if (
		'object' !== typeof state.jetpack.connection.status.siteConnected ||
		true === state.jetpack.connection.status.siteConnected.offlineMode.isActive
	) {
		return false;
	}
	return state.jetpack.connection.status.siteConnected.isActive;
}

/**
 * Checks if the site is registered with WordPress.com.
 *
 * @param {object} state -- Global state tree
 * @return {boolean} True if site is registered WordPress.com (has blog token). False if site is in Offline Mode or there's no connection data.
 */
export function isSiteRegistered( state ) {
	if (
		'object' !== typeof state.jetpack.connection.status.siteConnected ||
		true === state.jetpack.connection.status.siteConnected.offlineMode.isActive
	) {
		return false;
	}
	return state.jetpack.connection.status.siteConnected.isRegistered;
}

/**
 * Returns an object with information about the Offline Mode.
 *
 * @param {object} state - Global state tree
 * @return {boolean|object} False if site is not in Offline Mode. If it is, returns an object with information about the Offline Mode.
 */
export function getSiteOfflineMode( state ) {
	if ( state.jetpack.connection.status?.siteConnected?.offlineMode?.isActive ) {
		return state.jetpack.connection.status.siteConnected.offlineMode;
	}
	return false;
}

/**
 * Returns string/URL to make a connection to WordPress.com
 *
 * @param {object} state - Global state tree
 * @return {string}       URL for connecting to WordPress.com
 */
export function getConnectUrl( state ) {
	return state.jetpack.connection.connectUrl;
}

/**
 * Returns an object with information about the WP.com connected user
 *
 * @param {object} state - Global state tree
 * @return {object}       Returns an object with information about the connected user
 */
export function getConnectedWpComUser( state ) {
	return state.jetpack.connection.user.currentUser?.wpcomUser;
}

/**
 * Returns true if currently disconnecting the site
 *
 * @param {object} state - Global state tree
 * @return {boolean}         True if site is being disconnected
 */
export function isDisconnectingSite( state ) {
	return !! state.jetpack.connection.requests.disconnectingSite;
}

/**
 * Returns true if currently fetching connectUrl
 *
 * @param {object} state - Global state tree
 * @return {boolean} true if currently fetching connectUrl, false otherwise
 */
export function isFetchingConnectUrl( state ) {
	return !! state.jetpack.connection.requests.fetchingConnectUrl;
}

/**
 * Returns true if currently unlinking the user
 *
 * @param {object} state - Global state tree
 * @return {boolean} true if currently unlinking a user, false otherwise
 */
export function isUnlinkingUser( state ) {
	return !! state.jetpack.connection.requests.unlinkingUser;
}

/**
 * Returns true if currently linking the user
 *
 * @param {object} state - Global state tree
 * @return {boolean} true if currently linking a user, false otherwise
 */
export function isConnectingUser( state ) {
	return !! state.jetpack.connection.requests.connectingUser;
}

/**
 * Returns the feature label the user connection where initiated from, if any.
 *
 * @param {object} state - Global state tree
 * @return {string|null} string if feature label exists, false otherwise.
 */
export function getConnectingUserFeatureLabel( state ) {
	return Object.hasOwn( state.jetpack.connection.requests, 'connectingUserFeatureLabel' )
		? state.jetpack.connection.requests.connectingUserFeatureLabel
		: null;
}

/**
 * Returns the "from" value the user connection where initiated from, if any.
 *
 * @param {object} state - Global state tree
 * @return {string|null} string if "from" value exists, false otherwise.
 */
export function getConnectingUserFrom( state ) {
	return Object.hasOwn( state.jetpack.connection.requests, 'connectingUserFrom' )
		? state.jetpack.connection.requests.connectingUserFrom
		: null;
}

/**
 * Returns true if currently fetching user data
 *
 * @param {object} state - Global state tree
 * @return {boolean} true if currently fetching user data, false otherwise
 */
export function isFetchingUserData( state ) {
	return !! state.jetpack.connection.requests.fetchingUserData;
}

/**
 * Returns true if current user is linked to WordPress.com
 *
 * @param {object} state - Global state tree
 * @return {boolean} true if the current user is connected to WP.com, false otherwise
 */
export function isCurrentUserLinked( state ) {
	return !! state.jetpack.connection.user.currentUser.isConnected;
}

/**
 * Returns true if current user is connection owner.
 *
 * @param {object} state - Global state tree
 * @return {boolean} true if the current user is connection owner, false otherwise
 */
export function isConnectionOwner( state ) {
	return !! state.jetpack.connection.user.currentUser.isMaster;
}

/**
 * Returns connection owner name.
 *
 * @param {object} state - Global state tree
 * @return {string} Return connection owner name.
 */
export function isConnectionOwnerName( state ) {
	return state.jetpack.connection.user?.connectionOwner;
}

/**
 * Returns true if the site has a connected owner.
 *
 * @param {object} state - Global state tree
 * @return {boolean} true if the site has an owner connected, false otherwise
 */
export function hasConnectedOwner( state ) {
	return state.jetpack.connection.status?.siteConnected?.hasConnectedOwner ?? false;
}

/**
 * Checks if the site is currently in offline mode.
 *
 * @param {object} state - Global state tree
 * @return {boolean} True if site is in offline mode. False otherwise.
 */
export function isOfflineMode( state ) {
	return 'offline' === getSiteConnectionStatus( state );
}

/**
 * Checks if the site is currently in an Identity Crisis.
 *
 * @param {object} state - Global state tree
 * @return {boolean} True if site is in IDC. False otherwise.
 */
export function isInIdentityCrisis( state ) {
	return state.jetpack.connection.status?.siteConnected?.isInIdentityCrisis ?? false;
}

/**
 * Checks if the module requires connection.
 *
 * @param {object} state - Global state tree
 * @param {string} slug  - Module slug.
 * @return {boolean} True if module requires connection.
 */
export function requiresConnection( state, slug ) {
	return getModulesThatRequireConnection( state ).concat( [ 'backups', 'scan' ] ).includes( slug );
}

/**
 * Checks if the current module is unavailable in offline mode.
 *
 * @param {object} state  - Global state tree
 * @param {string} module - Module slug.
 * @return {boolean} True if site is in offline mode and module requires connection. False otherwise.
 */
export function isUnavailableInOfflineMode( state, module ) {
	return isOfflineMode( state ) && requiresConnection( state, module );
}

/**
 * Checks if the module requires user to be connected.
 *
 * @param {object} state - Global state tree
 * @param {string} slug  - Module slug.
 * @return {boolean} True if module requires connection.
 */
export function requiresUserConnection( state, slug ) {
	return getModulesThatRequireUserConnection( state ).includes( slug );
}

/**
 * Checks if the current module is unavailable in Site Connection mode.
 *
 * @param {object} state  - Global state tree
 * @param {string} module - Module slug.
 * @return {boolean} True if site is in Site Connection mode and module requires connection. False otherwise.
 */
export function isUnavailableInSiteConnectionMode( state, module ) {
	return ! hasConnectedOwner( state ) && requiresUserConnection( state, module );
}

/**
 * Checks if the JETPACK__SANDBOX_DOMAIN is set
 *
 * @param {object} state - Global state tree
 * @return {string} Value of the JETPACK__SANDBOX_DOMAIN constant. Empty string if not sandboxed - url if so.
 */
export function getSandboxDomain( state ) {
	return state.jetpack.connection.status?.siteConnected?.sandboxDomain ?? '';
}

/**
 * Check if the reconnect requested.
 *
 * @param {object} state - Global state tree.
 * @return {boolean} True if the reconnecting is required, false otherwise.
 */
export function isReconnectingSite( state ) {
	return !! state.jetpack.connection.requests.reconnectingSite;
}

/**
 * Check if `hasSeenWCConnectionModal` (Jetpack option) is true.
 *
 * @param {object} state - Global state tree.
 * @return {boolean} If true, the site has already displayed the WooCommerce Connection Modal.
 */
export function getHasSeenWCConnectionModal( state ) {
	return !! state.jetpack.connection.hasSeenWCConnectionModal;
}
