import Foundation

public enum RFC2047Decoder {
	/// Decode RFC 2047 encoded words in a string.
	/// Pattern: =?charset?encoding?encoded_text?=
	public static func decode(_ input: String) -> String {
		let pattern = #"=\?([^?]+)\?([BbQq])\?([^?]*)\?="#
		guard let regex = try? NSRegularExpression(pattern: pattern) else {
			return input
		}

		let nsInput = input as NSString
		let matches = regex.matches(in: input, range: NSRange(location: 0, length: nsInput.length))

		guard !matches.isEmpty else { return input }

		var result = ""
		var lastEnd = 0

		for match in matches {
			let matchRange = match.range
			// Add any non-encoded text between matches (skip whitespace between adjacent encoded words)
			let gap = nsInput.substring(with: NSRange(location: lastEnd, length: matchRange.location - lastEnd))
			let trimmedGap = gap.trimmingCharacters(in: .whitespaces)
			if !trimmedGap.isEmpty || lastEnd == 0 {
				// Only add gap if it's not just whitespace between encoded words
				if lastEnd == 0 && matchRange.location > 0 {
					result += gap
				} else if !trimmedGap.isEmpty {
					result += gap
				}
			}

			let charset = nsInput.substring(with: match.range(at: 1))
			let encoding = nsInput.substring(with: match.range(at: 2)).uppercased()
			let encodedText = nsInput.substring(with: match.range(at: 3))

			let cfEncoding = CFStringConvertIANACharSetNameToEncoding(charset as CFString)
			let nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding)

			let decoded: String?
			if encoding == "B" {
				guard let data = Data(base64Encoded: encodedText) else {
					result += nsInput.substring(with: matchRange)
					lastEnd = matchRange.location + matchRange.length
					continue
				}
				decoded = String(data: data, encoding: String.Encoding(rawValue: nsEncoding))
			} else {
				// Q encoding: like quoted-printable but underscores represent spaces
				let withSpaces = encodedText.replacingOccurrences(of: "_", with: " ")
				let data = decodeQuotedPrintableBytes(withSpaces)
				decoded = String(data: data, encoding: String.Encoding(rawValue: nsEncoding))
			}

			result += decoded ?? nsInput.substring(with: matchRange)
			lastEnd = matchRange.location + matchRange.length
		}

		// Append any trailing non-encoded text
		if lastEnd < nsInput.length {
			result += nsInput.substring(from: lastEnd)
		}

		return result
	}

	private static func decodeQuotedPrintableBytes(_ input: String) -> Data {
		var data = Data()
		var i = input.startIndex
		while i < input.endIndex {
			if input[i] == "=" {
				let hexStart = input.index(after: i)
				guard hexStart < input.endIndex else {
					data.append(contentsOf: "=".utf8)
					break
				}
				let hexEnd = input.index(hexStart, offsetBy: 1, limitedBy: input.endIndex) ?? input.endIndex
				guard hexEnd < input.endIndex else {
					data.append(contentsOf: String(input[i...]).utf8)
					break
				}
				let nextAfterHex = input.index(after: hexEnd)
				let hex = String(input[hexStart...hexEnd])
				if let byte = UInt8(hex, radix: 16) {
					data.append(byte)
					i = nextAfterHex
				} else {
					data.append(contentsOf: "=".utf8)
					i = hexStart
				}
			} else {
				data.append(contentsOf: String(input[i]).utf8)
				i = input.index(after: i)
			}
		}
		return data
	}
}
