import { regionData } from 'element-china-area-data';

const GENERIC_CITY_LABELS = new Set([
	'市辖区',
	'县',
	'自治区直辖县级行政区划',
	'省直辖县级行政区划'
]);

const NAME_LABELS = ['收货人', '收件人', '联系人', '姓名'];
const PHONE_LABELS = ['手机号码', '手机号', '联系电话', '联系方式', '电话', '手机'];
const REGION_LABELS = ['所在地区', '地区'];
const DETAIL_LABELS = ['详细地址', '收货地址', '地址'];
const ALL_FIELD_LABELS = [
	...new Set([...NAME_LABELS, ...PHONE_LABELS, ...REGION_LABELS, ...DETAIL_LABELS])
];

const PHONE_REG = /(?<!\d)(?:\+?86[-\s]?)?(1[3-9](?:[\s-]?\d){9})(?!\d)/;
const ADDRESS_SIGNAL_REG = /(省|市|区|县|旗|乡|镇|街道|大道|路|街|巷|弄|村|社区|小区|号|栋|单元|室|楼)/;
const FIELD_LABEL_PATTERN = createLabelPattern(ALL_FIELD_LABELS);
const STRIP_FIELD_LABEL_REG = new RegExp(
	`(^|[\\s,，;；\\n])(?:${FIELD_LABEL_PATTERN})\\s*[：:]`,
	'g'
);

const provinceOptions = regionData.map(({ label, value }) => ({ label, value }));

const districtRecords = regionData.flatMap((province) =>
	(province.children || []).flatMap((city) =>
		(city.children || []).map((district) => {
			const provinceAliases = createProvinceAliases(province.label);
			const cityAliases = createCityAliases(getDisplayCityLabel(city.label));
			const districtAliases = createDistrictAliases(district.label);

			return {
				province,
				city,
				district,
				provinceAliases,
				cityAliases,
				districtAliases,
				provinceSearchAliases: provinceAliases.map(normalizeForSearch).filter(Boolean),
				citySearchAliases: cityAliases.map(normalizeForSearch).filter(Boolean),
				districtSearchAliases: districtAliases.map(normalizeForSearch).filter(Boolean),
				fullSearchLabels: buildFullSearchLabels(provinceAliases, cityAliases, districtAliases)
			};
		})
	)
);

function escapeRegExp(value = '') {
	return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function createLabelPattern(labels = []) {
	return labels
		.slice()
		.sort((left, right) => right.length - left.length)
		.map((label) => escapeRegExp(label))
		.join('|');
}

function normalizeText(value = '') {
	return String(value)
		.replace(/\r/g, '\n')
		.replace(/\u00a0/g, ' ')
		.replace(/\t/g, ' ')
		.trim();
}

function normalizeForSearch(value = '') {
	return normalizeText(value).replace(/[\s,，.。;；:：、\-_/]/g, '');
}

function stripProvinceSuffix(label = '') {
	return normalizeText(label).replace(
		/(特别行政区|维吾尔自治区|壮族自治区|回族自治区|自治区|省|市)$/u,
		''
	);
}

function stripCitySuffix(label = '') {
	return normalizeText(label).replace(/(自治州|地区|盟|市)$/u, '');
}

function stripDistrictSuffix(label = '') {
	return normalizeText(label).replace(/(自治县|自治旗|区|县|旗|市)$/u, '');
}

function createAliases(label = '', stripFn = null) {
	const rawLabel = normalizeText(label);
	const aliases = new Set([rawLabel]);
	const simplifiedLabel = stripFn ? normalizeText(stripFn(rawLabel)) : '';

	if (simplifiedLabel && simplifiedLabel !== rawLabel) {
		aliases.add(simplifiedLabel);
	}

	return [...aliases].filter(Boolean);
}

function createProvinceAliases(label = '') {
	return createAliases(label, stripProvinceSuffix);
}

function createCityAliases(label = '') {
	if (!label) return [''];
	return createAliases(label, stripCitySuffix);
}

function createDistrictAliases(label = '') {
	return createAliases(label, stripDistrictSuffix);
}

function buildFullSearchLabels(provinceAliases = [], cityAliases = [], districtAliases = []) {
	const fullLabels = new Set();
	const normalizedCityAliases = cityAliases.length ? cityAliases : [''];

	provinceAliases.forEach((provinceAlias) => {
		normalizedCityAliases.forEach((cityAlias) => {
			districtAliases.forEach((districtAlias) => {
				const combinedLabel = normalizeForSearch(`${provinceAlias}${cityAlias}${districtAlias}`);
				if (combinedLabel) {
					fullLabels.add(combinedLabel);
				}
			});
		});
	});

	return [...fullLabels];
}

function getDisplayCityLabel(label = '') {
	return GENERIC_CITY_LABELS.has(label) ? '' : label;
}

function getProvinceByCode(provinceCode) {
	return regionData.find((item) => item.value === provinceCode) || null;
}

function getCityByCode(provinceCode, cityCode) {
	const province = getProvinceByCode(provinceCode);
	return province?.children?.find((item) => item.value === cityCode) || null;
}

function getDistrictByCode(provinceCode, cityCode, districtCode) {
	const city = getCityByCode(provinceCode, cityCode);
	return city?.children?.find((item) => item.value === districtCode) || null;
}

function getCityOptions(provinceCode) {
	const province = getProvinceByCode(provinceCode) || regionData[0];
	return (province?.children || []).map(({ label, value }) => ({ label, value }));
}

function getDistrictOptions(provinceCode, cityCode) {
	const province = getProvinceByCode(provinceCode) || regionData[0];
	const city =
		province?.children?.find((item) => item.value === cityCode) ||
		province?.children?.[0] ||
		null;

	return (city?.children || []).map(({ label, value }) => ({ label, value }));
}

function getLabelParts(regionCodes = []) {
	const [provinceCode, cityCode, districtCode] = regionCodes;
	const province = getProvinceByCode(provinceCode);
	const city = getCityByCode(provinceCode, cityCode);
	const district = getDistrictByCode(provinceCode, cityCode, districtCode);

	if (!province || !city || !district) {
		return [];
	}

	const displayCityLabel = getDisplayCityLabel(city.label);
	return [province.label, displayCityLabel, district.label].filter(Boolean);
}

function stripFieldLabels(value = '') {
	return normalizeText(value).replace(STRIP_FIELD_LABEL_REG, '$1');
}

function cleanupAddressText(value = '') {
	return stripFieldLabels(value)
		.replace(/[：:]/g, ' ')
		.replace(/[，,;；]/g, ' ')
		.replace(/\s+/g, ' ')
		.trim();
}

function cleanupName(value = '') {
	return normalizeText(value)
		.replace(/^[\s,，;；:：]+/, '')
		.replace(/[\s,，;；:：]+$/, '')
		.trim();
}

function findBestAliasInText(text = '', aliases = []) {
	return aliases
		.slice()
		.sort((left, right) => right.length - left.length)
		.find((alias) => alias && text.includes(alias)) || '';
}

function extractPhone(rawText = '') {
	const text = normalizeText(rawText);
	const match = text.match(PHONE_REG);
	if (!match) {
		return {
			phone: '',
			textWithoutPhone: text
		};
	}

	const digits = match[1].replace(/\D/g, '');
	const phone = digits.length > 11 ? digits.slice(-11) : digits;

	return {
		phone,
		textWithoutPhone: text.replace(match[0], ' ').replace(/\s+/g, ' ').trim()
	};
}

function pickBestMatch(records, matcher) {
	return records
		.filter(matcher)
		.sort((left, right) => {
			return (
				right.fullSearchLabels[0]?.length - left.fullSearchLabels[0]?.length ||
				right.district.label.length - left.district.label.length
			);
		})[0] || null;
}

function matchAnyAlias(searchText = '', aliases = []) {
	return aliases.some((alias) => alias && searchText.includes(alias));
}

function findRegionMatch(rawText = '') {
	const searchText = normalizeForSearch(rawText);
	if (!searchText) {
		return null;
	}

	let match = pickBestMatch(districtRecords, (record) => {
		return record.fullSearchLabels.some((label) => searchText.includes(label));
	});

	if (!match) {
		match = pickBestMatch(districtRecords, (record) => {
			return (
				matchAnyAlias(searchText, record.provinceSearchAliases) &&
				matchAnyAlias(searchText, record.districtSearchAliases) &&
				(!record.citySearchAliases.length || matchAnyAlias(searchText, record.citySearchAliases))
			);
		});
	}

	if (!match) {
		match = pickBestMatch(districtRecords, (record) => {
			return (
				matchAnyAlias(searchText, record.citySearchAliases) &&
				matchAnyAlias(searchText, record.districtSearchAliases)
			);
		});
	}

	return match;
}

function getRegionStartIndex(text, match) {
	const candidates = [
		findBestAliasInText(text, match.provinceAliases),
		findBestAliasInText(text, match.cityAliases),
		findBestAliasInText(text, match.districtAliases)
	]
		.filter(Boolean)
		.map((label) => text.indexOf(label))
		.filter((index) => index >= 0);

	return candidates.length ? Math.min(...candidates) : -1;
}

function stripMatchedRegion(addressText, match) {
	let detailAddress = normalizeText(addressText);
	const matchedProvince = findBestAliasInText(detailAddress, match.provinceAliases);
	const matchedCity = findBestAliasInText(detailAddress, match.cityAliases);
	const matchedDistrict = findBestAliasInText(detailAddress, match.districtAliases);

	[matchedProvince, matchedCity, matchedDistrict]
		.filter(Boolean)
		.forEach((label) => {
			detailAddress = detailAddress.replace(label, '');
		});

	return cleanupAddressText(detailAddress);
}

function extractNameFromText(rawText = '', textWithoutPhone = '', regionStartIndex = -1) {
	const sourceText =
		regionStartIndex > 0 ? textWithoutPhone.slice(0, regionStartIndex) : textWithoutPhone || rawText;

	const directNameMatch = normalizeText(rawText).match(
		/(?:收货人|收件人|联系人|姓名)\s*[：:]?\s*([A-Za-z\u4e00-\u9fa5·]{2,20})/u
	);
	if (directNameMatch?.[1]) {
		return directNameMatch[1].trim();
	}

	const cleaned = cleanupAddressText(sourceText)
		.replace(PHONE_REG, ' ')
		.replace(/\d+/g, ' ')
		.trim();
	const tokens = cleaned.split(/\s+/).filter(Boolean);
	const nameToken = tokens.find((token) => {
		return !ADDRESS_SIGNAL_REG.test(token) && /^[A-Za-z\u4e00-\u9fa5·]{2,20}$/u.test(token);
	});

	return nameToken || '';
}

function extractAddressSource(textWithoutPhone = '', name = '', match = null) {
	let addressSource = normalizeText(textWithoutPhone);

	if (name) {
		addressSource = addressSource.replace(name, ' ').replace(/\s+/g, ' ').trim();
	}

	if (match) {
		const regionStartIndex = getRegionStartIndex(addressSource, match);
		if (regionStartIndex >= 0) {
			return addressSource.slice(regionStartIndex).trim();
		}
	}

	return cleanupAddressText(addressSource);
}

function formatRegionCodes(match) {
	return [match.province.value, match.city.value, match.district.value];
}

function combineDetailParts(parts = []) {
	return parts
		.map((item) => cleanupAddressText(item))
		.filter(Boolean)
		.join('');
}

function extractFieldValue(text = '', labels = []) {
	if (!text || !labels.length) {
		return '';
	}

	const labelPattern = createLabelPattern(labels);
	const fieldReg = new RegExp(
		`(?:${labelPattern})\\s*[：:]\\s*([\\s\\S]*?)(?=(?:[\\n,，]\\s*)?(?:${FIELD_LABEL_PATTERN})\\s*[：:]|$)`,
		'u'
	);
	const match = normalizeText(text).match(fieldReg);

	if (!match?.[1]) {
		return '';
	}

	return normalizeText(match[1]).replace(/^[,，\s]+|[,，\s]+$/g, '');
}

function extractLabeledFields(text = '') {
	return {
		name: extractFieldValue(text, NAME_LABELS),
		phone: extractFieldValue(text, PHONE_LABELS),
		region: extractFieldValue(text, REGION_LABELS),
		detail: extractFieldValue(text, DETAIL_LABELS)
	};
}

function parseFieldBasedShippingText(text = '') {
	const fields = extractLabeledFields(text);
	const hasAnyField = Object.values(fields).some(Boolean);

	if (!hasAnyField) {
		return null;
	}

	const recipientName = cleanupName(fields.name);
	const recipientPhone = extractPhone(fields.phone || text).phone;
	const regionSource = normalizeText(fields.region);
	const detailSource = normalizeText(fields.detail);
	let regionMatch = findRegionMatch(regionSource || `${detailSource}${text}`);

	if (!regionMatch) {
		regionMatch = findRegionMatch(text);
	}

	const detailParts = [];
	if (regionSource) {
		const regionRemainder = regionMatch ? stripMatchedRegion(regionSource, regionMatch) : regionSource;
		if (regionRemainder) {
			detailParts.push(regionRemainder);
		}
	}
	if (detailSource) {
		detailParts.push(detailSource);
	}

	return {
		recipient_name: recipientName,
		recipient_phone: recipientPhone,
		regionCodes: regionMatch ? formatRegionCodes(regionMatch) : [],
		detailAddress: combineDetailParts(detailParts)
	};
}

export function buildRegionColumns(regionCodes = []) {
	const provinceCode =
		getProvinceByCode(regionCodes[0])?.value || provinceOptions[0]?.value || '';
	const cityOptions = getCityOptions(provinceCode);
	const cityCode =
		cityOptions.find((item) => item.value === regionCodes[1])?.value ||
		cityOptions[0]?.value ||
		'';
	const districtOptions = getDistrictOptions(provinceCode, cityCode);
	const districtCode =
		districtOptions.find((item) => item.value === regionCodes[2])?.value ||
		districtOptions[0]?.value ||
		'';

	return {
		columns: [provinceOptions, cityOptions, districtOptions],
		defaultIndex: [
			provinceOptions.findIndex((item) => item.value === provinceCode),
			cityOptions.findIndex((item) => item.value === cityCode),
			districtOptions.findIndex((item) => item.value === districtCode)
		]
	};
}

export function getRegionDisplayText(regionCodes = []) {
	return getLabelParts(regionCodes).join(' / ');
}

export function composeRecipientAddress(regionCodes = [], detailAddress = '') {
	const regionText = getLabelParts(regionCodes).join('');
	return `${regionText}${normalizeText(detailAddress)}`.trim();
}

export function parseSavedRecipientAddress(rawAddress = '') {
	const addressText = normalizeText(rawAddress);
	if (!addressText) {
		return {
			regionCodes: [],
			detailAddress: ''
		};
	}

	const match = findRegionMatch(addressText);
	if (!match) {
		return {
			regionCodes: [],
			detailAddress: addressText
		};
	}

	const detailAddress = stripMatchedRegion(addressText, match);
	return {
		regionCodes: formatRegionCodes(match),
		detailAddress: detailAddress || addressText
	};
}

export function parseShippingText(rawText = '') {
	const text = normalizeText(rawText);
	if (!text) {
		return {
			recipient_name: '',
			recipient_phone: '',
			regionCodes: [],
			detailAddress: ''
		};
	}

	const fieldBasedResult = parseFieldBasedShippingText(text);
	if (fieldBasedResult) {
		return fieldBasedResult;
	}

	const { phone, textWithoutPhone } = extractPhone(text);
	const regionMatch = findRegionMatch(textWithoutPhone);
	const regionStartIndex = regionMatch ? getRegionStartIndex(textWithoutPhone, regionMatch) : -1;
	const recipientName = extractNameFromText(text, textWithoutPhone, regionStartIndex);
	const addressSource = extractAddressSource(textWithoutPhone, recipientName, regionMatch);
	const detailAddress = regionMatch
		? stripMatchedRegion(addressSource, regionMatch)
		: cleanupAddressText(addressSource);

	return {
		recipient_name: recipientName,
		recipient_phone: phone,
		regionCodes: regionMatch ? formatRegionCodes(regionMatch) : [],
		detailAddress
	};
}
