import Foundation
@_exported import MailStore
import GRDB
import Models

/// TaskStore manages VTODO files on disk and their corresponding cache in SQLite.
public struct TaskStore: Sendable {
	public let taskDirectory: URL
	public let dbWriter: any DatabaseWriter

	public init(taskDirectory: URL, dbWriter: any DatabaseWriter) {
		self.taskDirectory = taskDirectory
		self.dbWriter = dbWriter
	}

	// MARK: - Write

	/// Write a task as a .ics file and upsert the cache record.
	public func writeTask(
		id: String,
		accountId: String,
		summary: String,
		description: String? = nil,
		status: String = "NEEDS-ACTION",
		priority: Int = 0,
		dueDate: Date? = nil,
		deferUntil: Date? = nil,
		linkedMessageId: String? = nil,
		categories: [String]? = nil,
		isSomeday: Bool = false
	) throws {
		let now = Date()

		// Preserve original createdAt if updating existing task
		let createdAt: Date
		let existing = try dbWriter.read { db in
			try TaskRecord.fetchOne(db, key: id)
		}
		if let existing, let parsed = VTODOParser.parseDate(existing.createdAt) {
			createdAt = parsed
		} else {
			createdAt = now
		}

		let icsContent = VTODOFormatter.format(
			uid: id,
			summary: summary,
			description: description,
			status: status,
			priority: priority,
			dueDate: dueDate,
			deferUntil: deferUntil,
			categories: categories,
			linkedMessageId: linkedMessageId,
			isSomeday: isSomeday,
			createdAt: createdAt,
			updatedAt: now
		)

		// Ensure directory exists
		try FileManager.default.createDirectory(at: taskDirectory, withIntermediateDirectories: true)

		let filePath = taskDirectory.appendingPathComponent("\(id).ics")
		try icsContent.write(to: filePath, atomically: true, encoding: .utf8)

		// Upsert cache record
		var record = TaskRecord(
			id: id,
			accountId: accountId,
			summary: summary,
			description: description,
			status: status,
			priority: priority,
			dueDate: dueDate.map { VTODOParser.formatDateOnly($0) },
			deferUntil: deferUntil.map { VTODOParser.formatDateOnly($0) },
			createdAt: VTODOParser.formatDate(createdAt),
			updatedAt: VTODOParser.formatDate(now),
			filePath: filePath.path,
			linkedMessageId: linkedMessageId,
			isSomeday: isSomeday
		)
		try dbWriter.write { db in
			try record.save(db)
		}
	}

	// MARK: - Update status

	/// Update the status of a task in both the cache and the .ics file.
	public func updateStatus(id: String, status: String) throws {
		guard var record = try dbWriter.read({ db in try TaskRecord.fetchOne(db, key: id) }) else {
			return
		}

		record.status = status
		record.updatedAt = VTODOParser.formatDate(Date())

		try dbWriter.write { db in
			try record.update(db)
		}

		try rewriteICSFile(for: record)
	}

	// MARK: - Update deferral

	/// Update the deferral date and someday flag for a task.
	public func updateDeferral(id: String, deferUntil: Date?, isSomeday: Bool) throws {
		guard var record = try dbWriter.read({ db in try TaskRecord.fetchOne(db, key: id) }) else {
			return
		}

		record.deferUntil = deferUntil.map { VTODOParser.formatDateOnly($0) }
		record.isSomeday = isSomeday
		record.updatedAt = VTODOParser.formatDate(Date())

		try dbWriter.write { db in
			try record.update(db)
		}

		try rewriteICSFile(for: record)
	}

	// MARK: - Delete

	/// Delete a task's .ics file and cache record.
	public func deleteTask(id: String) throws {
		let filePath = taskDirectory.appendingPathComponent("\(id).ics")
		try? FileManager.default.removeItem(at: filePath)

		try dbWriter.write { db in
			_ = try TaskRecord.deleteOne(db, key: id)
		}
	}

	// MARK: - Rebuild cache

	/// Clear the task cache and rebuild it from .ics files on disk.
	public func rebuildCache(accountId: String) throws {
		// Clear existing task records and their labels
		try dbWriter.write { db in
			_ = try TaskRecord.deleteAll(db)
			try db.execute(sql: "DELETE FROM itemLabel WHERE itemType = 'task'")
		}

		let fm = FileManager.default
		guard fm.fileExists(atPath: taskDirectory.path) else { return }

		let files = try fm.contentsOfDirectory(at: taskDirectory, includingPropertiesForKeys: nil)
			.filter { $0.pathExtension == "ics" }

		for file in files {
			let content = try String(contentsOf: file, encoding: .utf8)
			guard let props = VTODOParser.parse(content) else { continue }

			let uid = props["UID"] ?? file.deletingPathExtension().lastPathComponent
			let summary = props["SUMMARY"] ?? ""
			let description = props["DESCRIPTION"]
			let status = props["STATUS"] ?? "NEEDS-ACTION"
			let priority = Int(props["PRIORITY"] ?? "0") ?? 0
			let dueDate = props["DUE"]
			let deferUntil = props["DTSTART"]
			let createdAt = props["CREATED"] ?? VTODOParser.formatDate(Date())
			let updatedAt = props["LAST-MODIFIED"] ?? VTODOParser.formatDate(Date())
			let isSomeday = props["X-MAGNUM-SOMEDAY"] == "TRUE"

			// Extract linked message ID from ATTACH:mid:<id>
			let linkedMessageId: String?
			if let attach = props["ATTACH"], attach.hasPrefix("mid:") {
				linkedMessageId = String(attach.dropFirst(4))
			} else {
				linkedMessageId = nil
			}

			var record = TaskRecord(
				id: uid,
				accountId: accountId,
				summary: summary,
				description: description,
				status: status,
				priority: priority,
				dueDate: dueDate,
				deferUntil: deferUntil,
				createdAt: createdAt,
				updatedAt: updatedAt,
				filePath: file.path,
				linkedMessageId: linkedMessageId,
				isSomeday: isSomeday
			)

			try dbWriter.write { db in
				try record.save(db)
			}

			// Rebuild labels from CATEGORIES
			if let categoriesStr = props["CATEGORIES"] {
				let categories = categoriesStr.components(separatedBy: ",")
					.map { $0.trimmingCharacters(in: .whitespaces) }
					.filter { !$0.isEmpty }

				for categoryName in categories {
					try dbWriter.write { db in
						// Find or create label
						var label = try LabelRecord
							.filter(Column("name") == categoryName)
							.filter(Column("accountId") == accountId)
							.fetchOne(db)

						if label == nil {
							label = LabelRecord(
								id: UUID().uuidString,
								accountId: accountId,
								name: categoryName
							)
							try label!.insert(db)
						}

						let itemLabel = ItemLabelRecord(
							labelId: label!.id,
							itemType: "task",
							itemId: uid
						)
						try itemLabel.save(db)
					}
				}
			}
		}
	}

	// MARK: - Private

	/// Rewrite the .ics file from a TaskRecord's current state.
	private func rewriteICSFile(for record: TaskRecord) throws {
		let createdAt = VTODOParser.parseDate(record.createdAt) ?? Date()
		let updatedAt = VTODOParser.parseDate(record.updatedAt) ?? Date()

		// Read existing file to preserve categories
		var categories: [String]? = nil
		let filePath = taskDirectory.appendingPathComponent("\(record.id).ics")
		if let existingContent = try? String(contentsOf: filePath, encoding: .utf8),
		   let props = VTODOParser.parse(existingContent),
		   let cats = props["CATEGORIES"] {
			categories = cats.components(separatedBy: ",")
				.map { $0.trimmingCharacters(in: .whitespaces) }
				.filter { !$0.isEmpty }
		}

		let icsContent = VTODOFormatter.format(
			uid: record.id,
			summary: record.summary,
			description: record.description,
			status: record.status,
			priority: record.priority,
			dueDate: record.dueDate.flatMap { VTODOParser.parseDate($0) },
			deferUntil: record.deferUntil.flatMap { VTODOParser.parseDate($0) },
			categories: categories,
			linkedMessageId: record.linkedMessageId,
			isSomeday: record.isSomeday,
			createdAt: createdAt,
			updatedAt: updatedAt
		)

		try FileManager.default.createDirectory(at: taskDirectory, withIntermediateDirectories: true)
		try icsContent.write(to: filePath, atomically: true, encoding: .utf8)
	}
}
