import Foundation
import GRDB

/// Simplified JWZ thread reconstruction.
/// Links messages by Message-ID, In-Reply-To, and References headers.
/// No subject-based fallback (produces false matches).
public struct ThreadReconstructor: Sendable {
	private let store: MailStore

	public init(store: MailStore) {
		self.store = store
	}

	/// Process newly inserted messages and assign them to threads.
	public func processMessages(_ messages: [MessageRecord]) throws {
		for message in messages {
			try processOneMessage(message)
		}
	}

	private func processOneMessage(_ message: MessageRecord) throws {
		// Collect all related Message-IDs from In-Reply-To and References
		var relatedIds = Set<String>()
		if let inReplyTo = message.inReplyTo, !inReplyTo.isEmpty {
			relatedIds.insert(inReplyTo)
		}
		if let refs = message.refs, !refs.isEmpty {
			for ref in refs.split(separator: " ") {
				let trimmed = ref.trimmingCharacters(in: .whitespaces)
				if !trimmed.isEmpty {
					relatedIds.insert(trimmed)
				}
			}
		}
		if let mid = message.messageId, !mid.isEmpty {
			relatedIds.insert(mid)
		}

		// Find existing threads that contain any of these Message-IDs
		let matchingThreadIds = try store.findThreadsByMessageIds(relatedIds)

		let threadId: String
		if matchingThreadIds.isEmpty {
			// No existing thread — create a new one
			threadId = UUID().uuidString
			let subject = stripReplyPrefix(message.subject)
			try store.insertThread(ThreadRecord(
				id: threadId,
				accountId: message.accountId,
				subject: subject,
				lastDate: message.date,
				messageCount: 1
			))
		} else if matchingThreadIds.count == 1 {
			// Exactly one matching thread — add to it
			threadId = matchingThreadIds[0]
			try updateThreadMetadata(threadId: threadId, newMessage: message)
		} else {
			// Multiple matching threads — merge them, then add message
			try store.mergeThreads(matchingThreadIds)
			threadId = matchingThreadIds[0]
			try updateThreadMetadata(threadId: threadId, newMessage: message)
		}

		// Link message to thread
		try store.linkMessageToThread(threadId: threadId, messageId: message.id)
	}

	private func updateThreadMetadata(threadId: String, newMessage: MessageRecord) throws {
		let existingMessageIds = try store.threadMessageIds(threadId: threadId)
		let newCount = existingMessageIds.count + 1
		let threads = try store.threads(accountId: newMessage.accountId)
		let currentThread = threads.first { $0.id == threadId }
		let lastDate = max(currentThread?.lastDate ?? "", newMessage.date)
		try store.updateThread(
			id: threadId,
			lastDate: lastDate,
			messageCount: newCount,
			subject: nil
		)
	}

	/// Strip Re:, Fwd:, and similar prefixes for thread subject normalization
	private func stripReplyPrefix(_ subject: String?) -> String? {
		guard var s = subject else { return nil }
		let prefixes = ["re:", "fwd:", "fw:"]
		var changed = true
		while changed {
			changed = false
			let trimmed = s.trimmingCharacters(in: .whitespaces)
			for prefix in prefixes {
				if trimmed.lowercased().hasPrefix(prefix) {
					s = String(trimmed.dropFirst(prefix.count))
					changed = true
					break
				}
			}
		}
		return s.trimmingCharacters(in: .whitespaces)
	}
}
