import { select } from '@wordpress/data'

export interface RibbitSessionBilling {
	first_name: string
	last_name: string
	email: string
	phone: string
	address_1: string
	address_2: string
	city: string
	state: string
	postcode: string
	country: string
}

type PartialBilling = Partial<RibbitSessionBilling>

const BILLING_FIELDS: (keyof RibbitSessionBilling)[] = [
	'first_name',
	'last_name',
	'email',
	'phone',
	'address_1',
	'address_2',
	'city',
	'state',
	'postcode',
	'country',
]

const trim = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')

const normalizeBilling = (partial: PartialBilling): RibbitSessionBilling => ({
	first_name: trim(partial.first_name),
	last_name: trim(partial.last_name),
	email: trim(partial.email),
	phone: trim(partial.phone),
	address_1: trim(partial.address_1),
	address_2: trim(partial.address_2),
	city: trim(partial.city),
	state: trim(partial.state),
	postcode: trim(partial.postcode),
	country: trim(partial.country) || 'US',
})

const mergeBillingSources = (...sources: PartialBilling[]): RibbitSessionBilling => {
	const merged: PartialBilling = {}

	for (const field of BILLING_FIELDS) {
		for (const source of sources) {
			const value = trim(source[field])
			if (value && !merged[field]) {
				merged[field] = value
			}
		}
	}

	return normalizeBilling(merged)
}

const getCartStoreBilling = (): PartialBilling => {
	try {
		const cartStore = select('wc/store/cart') as
			| {
					getCustomerData?: () => {
						billingAddress?: PartialBilling
						shippingAddress?: PartialBilling
					}
			  }
			| undefined

		if (!cartStore?.getCustomerData) {
			return {}
		}

		const customerData = cartStore.getCustomerData()
		const billingAddress = customerData?.billingAddress ?? {}
		const shippingAddress = customerData?.shippingAddress ?? {}

		const checkoutStore = select('wc/store/checkout') as
			| {
					getUseShippingAsBilling?: () => boolean
			  }
			| undefined

		const useShippingAsBilling = checkoutStore?.getUseShippingAsBilling?.() ?? false

		if (useShippingAsBilling) {
			return mergeBillingSources(shippingAddress, billingAddress)
		}

		return mergeBillingSources(billingAddress, shippingAddress)
	} catch {
		return {}
	}
}

const getFieldFromDom = (names: string[], ids: string[]): string => {
	for (const name of names) {
		const byName = document.querySelector<HTMLInputElement | HTMLSelectElement>(
			`[name="${name}"]`,
		)
		const value = trim(byName?.value)
		if (value) {
			return value
		}
	}

	for (const id of ids) {
		const byId = document.querySelector<HTMLInputElement | HTMLSelectElement>(`#${id}`)
		const value = trim(byId?.value)
		if (value) {
			return value
		}
	}

	return ''
}

const getDomBilling = (): PartialBilling => ({
	first_name: getFieldFromDom(
		['billing_first_name'],
		['billing_first_name', 'billing-first_name'],
	),
	last_name: getFieldFromDom(['billing_last_name'], ['billing_last_name', 'billing-last_name']),
	email: getFieldFromDom(['billing_email', 'email'], ['billing_email', 'billing-email', 'email']),
	phone: getFieldFromDom(['billing_phone', 'phone'], ['billing_phone', 'billing-phone', 'phone']),
	address_1: getFieldFromDom(
		['billing_address_1'],
		['billing_address_1', 'billing-address_1'],
	),
	address_2: getFieldFromDom(
		['billing_address_2'],
		['billing_address_2', 'billing-address_2'],
	),
	city: getFieldFromDom(['billing_city'], ['billing_city', 'billing-city']),
	state: getFieldFromDom(['billing_state'], ['billing_state', 'billing-state']),
	postcode: getFieldFromDom(
		['billing_postcode'],
		['billing_postcode', 'billing-postcode'],
	),
	country: getFieldFromDom(['billing_country'], ['billing_country', 'billing-country']),
})

export const getCheckoutBillingDetails = (): RibbitSessionBilling =>
	mergeBillingSources(getCartStoreBilling(), getDomBilling())

export const ribbitSessionBillingToApiParams = (
	billing: RibbitSessionBilling,
): Record<string, string> => ({
	FirstName: billing.first_name,
	LastName: billing.last_name,
	EmailAddress: billing.email,
	Phone: billing.phone,
	Address1: billing.address_1,
	Address2: billing.address_2,
	City: billing.city,
	State: billing.state,
	Zip: billing.postcode,
	Country: billing.country,
})

export const appendRibbitSessionBillingToFormData = (
	formData: FormData,
	billing: RibbitSessionBilling,
): void => {
	const params = ribbitSessionBillingToApiParams(billing)
	for (const [key, value] of Object.entries(params)) {
		formData.append(key, value)
	}
}
