import SwiftUI
import GRDB
import Models
import MailStore
import SyncEngine
import IMAPClient

@Observable
@MainActor
final class MailViewModel {
	private var store: MailStore?
	private var coordinator: SyncCoordinator?

	var threads: [ThreadSummary] = []
	var selectedThread: ThreadSummary?
	var messages: [MessageSummary] = []
	var mailboxes: [MailboxInfo] = []
	var selectedMailbox: MailboxInfo?
	var syncState: SyncState = .idle
	var errorMessage: String?

	private var threadObservation: Task<Void, Never>?
	private var messageObservation: Task<Void, Never>?

	var hasAccount: Bool {
		store != nil && coordinator != nil
	}

	func setup(config: AccountConfig, credentials: Credentials) throws {
		let dbPath = Self.databasePath(for: config.id)
		let dbPool = try DatabaseSetup.openDatabase(atPath: dbPath)
		let mailStore = MailStore(dbWriter: dbPool)
		let imapClient = IMAPClient(
			host: config.imapHost,
			port: config.imapPort,
			credentials: credentials
		)
		store = mailStore
		coordinator = SyncCoordinator(
			accountConfig: config,
			imapClient: imapClient,
			store: mailStore
		)
	}

	func loadMailboxes(accountId: String) async {
		guard let store else { return }
		do {
			let records = try store.mailboxes(accountId: accountId)
			mailboxes = records.map { record in
				MailboxInfo(
					id: record.id, accountId: record.accountId,
					name: record.name, unreadCount: 0, totalCount: 0
				)
			}
			if selectedMailbox == nil, let inbox = mailboxes.first(where: { $0.name.lowercased() == "inbox" }) {
				selectedMailbox = inbox
			}
		} catch {
			errorMessage = error.localizedDescription
		}
	}

	func startObservingThreads(accountId: String) {
		guard let store else { return }
		threadObservation?.cancel()
		threadObservation = Task {
			do {
				for try await summaries in store.observeThreadSummaries(accountId: accountId) {
					self.threads = summaries
				}
			} catch {
				if !Task.isCancelled {
					self.errorMessage = error.localizedDescription
				}
			}
		}
	}

	func selectThread(_ thread: ThreadSummary) {
		selectedThread = thread
		messageObservation?.cancel()
		guard let store else { return }
		messageObservation = Task {
			do {
				for try await msgs in store.observeMessages(threadId: thread.id) {
					self.messages = msgs
				}
			} catch {
				if !Task.isCancelled {
					self.errorMessage = error.localizedDescription
				}
			}
		}
	}

	func syncNow() async {
		guard let coordinator else { return }
		do {
			try await coordinator.syncNow()
			syncState = coordinator.syncState
		} catch {
			errorMessage = error.localizedDescription
			syncState = .error(error.localizedDescription)
		}
	}

	func startPeriodicSync() {
		coordinator?.startPeriodicSync()
	}

	func stopSync() {
		coordinator?.stopSync()
		threadObservation?.cancel()
		messageObservation?.cancel()
	}

	static func databasePath(for accountId: String) -> String {
		let dir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
			.appendingPathComponent("MagnumOpus", isDirectory: true)
		try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
		return dir.appendingPathComponent("\(accountId).sqlite").path
	}
}
