import { useCallback, useEffect, useMemo, useState } from 'react';
import { Alert, Box, Button, CircularProgress } from '@mui/material';
import { useDispatch, useSelector } from 'react-redux';
import {
	dispatchAlert,
	providersApi,
	useSaveConnectionSettingsMutation,
} from '../../../../redux/Actions/ProvidersAction';
import { useGetCountriesStatesQuery } from '../../../../redux/Actions/WooCommerceDataAction';
import { setAlert, setModal } from '../../../../redux/features/providers/providersSlice';
import {
	AccountStep,
	CompleteStep,
	EulaStep,
	ProofStep,
	SecureCodeStep,
	VerificationChoiceStep,
} from './Steps';

const STEPS = ['Agreement', 'Account', 'Verification', 'Proof', 'Confirm'];

const EULA = 'Agreement';
const ACCOUNT = 'Account';
const CHOICE = 'Verification';
const PROOF = 'Proof';
const CONFIRM = 'Confirm';

/**
 * Walks a merchant through FedEx account registration.
 *
 * FedEx issues a child connection under Eniture's own rather than the merchant supplying an API
 * key, so each step is a round trip to the web service: the billing address is checked, then
 * ownership is proven with either a code FedEx sends to the account holder or the details of a
 * recent invoice. The key and secret arrive at the end and are saved as an ordinary connection.
 */
const RegistrationWizard = ({ connSettings, setConnSettings, isReconnect }) => {
	const admin_obj = eniture_rtsq || {};
	const url = admin_obj?.eniture_rtsq_rest_url;
	const dispatch = useDispatch();
	const { modal, eniture_api_key } = useSelector(state => state.providers);
	const [save, { isLoading: isSending }] = useSaveConnectionSettingsMutation();
	const { data: countriesData } = useGetCountriesStatesQuery({ url });
	const countries = countriesData?.data || [];

	const [stepIndex, setStepIndex] = useState(0);
	const [accepted, setAccepted] = useState(false);
	const [errors, setErrors] = useState({});
	const [failure, setFailure] = useState('');
	const [mfa, setMfa] = useState({ account_token: '', phone_number: '', options: {} });
	// Set only when this run of the wizard produced credentials. Inferring it from the connection
	// would mistake a key left over from the REST form for a completed registration.
	const [completed, setCompleted] = useState(false);

	const [form, setForm] = useState({
		nickname: '',
		account_number: '',
		customer_name: '',
		residential_address: false,
		address: '',
		address_2: '',
		city: '',
		state: '',
		zip: '',
		country: 'US',
		validation_type: '',
		pin_option: '',
		secure_pin: '',
		invoice_number: '',
		invoice_date: '',
		invoice_amount: '',
		invoice_currency: 'USD',
	});

	useEffect(() => {
		setForm(prev => ({
			...prev,
			nickname: connSettings?.nickname || prev.nickname,
			account_number: connSettings?.account_number || prev.account_number,
		}));
	}, []);

	const step = STEPS[stepIndex];
	const onChange = useCallback((name, value) => {
		setForm(prev => ({ ...prev, [name]: value }));
		setErrors(prev => ({ ...prev, [name]: '' }));
	}, []);

	const required = useMemo(
		() => ({
			[ACCOUNT]: {
				nickname: 'Nickname is required.',
				account_number: 'Account number is required.',
				customer_name: 'Customer name is required.',
				address: 'Address is required.',
				city: 'City is required.',
				state: 'State is required.',
				zip: 'Postal Code is required.',
			},
		}),
		[]
	);

	const validate = () => {
		const rules = { ...(required[step] || {}) };

		if (step === PROOF && form.validation_type === 'invoice') {
			rules.invoice_number = 'Invoice number is required.';
			rules.invoice_date = 'Invoice date is required.';
			rules.invoice_amount = 'Invoice amount is required.';
		}
		if (step === CONFIRM && form.validation_type !== 'invoice') {
			rules.secure_pin = 'Security code is required.';
		}

		const found = {};
		Object.entries(rules).forEach(([key, message]) => {
			if (!String(form[key] || '').trim()) found[key] = message;
		});

		if (!found.invoice_date && rules.invoice_date && !/^\d{4}-\d{2}-\d{2}$/.test(form.invoice_date)) {
			found.invoice_date = 'Format must be YYYY-MM-DD.';
		}
		if (!found.secure_pin && rules.secure_pin && form.secure_pin.length !== 6) {
			found.secure_pin = 'Enter the six digit code.';
		}

		setErrors(found);
		return Object.keys(found).length === 0;
	};

	const call = async payload => {
		setFailure('');
		const body = {
			...payload,
			eniture_api_key,
			provider: 'FedexSmall',
			action: 'save_connection_settings',
			...(isReconnect ? { reconnect: 1 } : {}),
			...(modal.data?.action === 'edit'
				? { installed_provider_uuid: modal.data?.uuid }
				: { provider_id: modal.data?.id }),
		};

		const res = await save({ url, ...body }).unwrap();
		// The REST layer is inconsistent about whether the reply arrives already unwrapped, so
		// find the envelope rather than assuming a depth.
		const envelope =
			res && res.data && typeof res.data === 'object' && ('error' in res.data || 'message' in res.data)
				? res.data
				: res;

		if (envelope?.error) {
			setFailure(envelope?.message || 'FedEx could not complete this step.');
			return null;
		}

		return envelope?.data ?? {};
	};

	const finish = async result => {
		const saved = await call({
			nickname: form.nickname,
			api_type: 'integrator',
			account_number: form.account_number,
			client_id: result.client_id,
			client_secret: result.client_secret,
			address: form.address,
			address_2: form.address_2,
			city: form.city,
			state: form.state,
			zip: form.zip,
			country: form.country,
		});

		if (saved === null) return;

		setConnSettings(prev => ({
			...prev,
			api_type: 'integrator',
			nickname: form.nickname,
			account_number: form.account_number,
			client_id: result.client_id,
			client_secret: result.client_secret,
		}));

		dispatch(providersApi.util.invalidateTags(['InstalledProviders']));
		dispatch(setAlert(dispatchAlert(true, 'Your FedEx account is connected.', 'success')));
		setCompleted(true);
		setStepIndex(STEPS.indexOf(CONFIRM));
	};

	const next = async () => {
		if (!validate()) return;

		if (step === EULA) {
			setStepIndex(STEPS.indexOf(ACCOUNT));
			return;
		}

		if (step === ACCOUNT) {
			const result = await call({
				integrator_step: 'address_validation',
				account_number: form.account_number,
				customer_name: form.customer_name,
				address: form.address,
				address_2: form.address_2,
				city: form.city,
				state: form.state,
				zip: form.zip,
				country: form.country,
				residential_address: form.residential_address ? 1 : 0,
			});
			if (result === null) return;

			if (result.registration_complete) {
				await finish(result);
				return;
			}

			setMfa({
				account_token: result.account_token || '',
				phone_number: result.phone_number || '',
				options: result.options || {},
			});
			const codes = (result.options?.secureCode || []).length > 0;
			setForm(prev => ({
				...prev,
				validation_type: codes ? 'code' : 'invoice',
				pin_option: (result.options?.secureCode || [])[0] || '',
			}));
			setStepIndex(STEPS.indexOf(CHOICE));
			return;
		}

		if (step === CHOICE) {
			setStepIndex(STEPS.indexOf(PROOF));
			return;
		}

		if (step === PROOF) {
			if (form.validation_type === 'invoice') {
				const result = await call({
					integrator_step: 'invoice_validation',
					account_token: mfa.account_token,
					invoice_number: form.invoice_number,
					invoice_date: form.invoice_date,
					invoice_amount: form.invoice_amount,
					invoice_currency: form.invoice_currency,
				});
				if (result === null) return;
				await finish(result);
				return;
			}

			const sent = await call({
				integrator_step: 'pin_generation',
				account_token: mfa.account_token,
				pin_option: form.pin_option,
			});
			if (sent === null) return;
			setStepIndex(STEPS.indexOf(CONFIRM));
			return;
		}

		if (step === CONFIRM) {
			const result = await call({
				integrator_step: 'pin_validation',
				account_token: mfa.account_token,
				secure_pin: form.secure_pin,
			});
			if (result === null) return;
			await finish(result);
		}
	};

	const isFinalCodeEntry = step === CONFIRM && form.validation_type !== 'invoice' && !completed;

	const body = () => {
		if (completed) return <CompleteStep accountNumber={form.account_number} />;
		if (step === EULA) return <EulaStep eulaUrl={`${admin_obj?.eniture_rtsq_url}/build/assets/fedex-eula.pdf`} accepted={accepted} onAccept={setAccepted} />;
		if (step === ACCOUNT) return <AccountStep state={form} errors={errors} onChange={onChange} countries={countries} />;
		if (step === CHOICE) return <VerificationChoiceStep state={form} options={mfa.options} onChange={onChange} />;
		if (step === PROOF) return <ProofStep state={form} errors={errors} options={mfa.options} phoneNumber={mfa.phone_number} onChange={onChange} />;
		return <SecureCodeStep state={form} errors={errors} onChange={onChange} />;
	};

	const canGoBack = stepIndex > 0 && !completed;
	const nextLabel = isFinalCodeEntry ? 'Connect account' : step === PROOF && form.validation_type === 'invoice' ? 'Connect account' : 'Next';

	return (
		<Box>

			{failure && (
				<Alert severity='error' sx={{ mb: 2 }}>
					{failure}
				</Alert>
			)}

			{body()}

			<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end', mt: 3 }}>
				{canGoBack && (
					<Button variant='outlined' onClick={() => setStepIndex(stepIndex - 1)} disabled={isSending}>
						Back
					</Button>
				)}
				{completed ? (
					<Button
						variant='contained'
						onClick={() => dispatch(setModal({ open: false, type: '', data: {} }))}>
						Done
					</Button>
				) : (
					<Button
						variant='contained'
						onClick={next}
						disabled={isSending || (step === EULA && !accepted)}>
						{isSending ? <CircularProgress size={20} sx={{ color: 'white' }} /> : nextLabel}
					</Button>
				)}
			</Box>
		</Box>
	);
};

export default RegistrationWizard;
