import Foundation

/// Formats task properties into iCalendar VTODO components.
public enum VTODOFormatter {

	/// Build a complete iCalendar document containing a single VTODO.
	public static func format(
		uid: String,
		summary: String,
		description: String? = nil,
		status: String = "NEEDS-ACTION",
		priority: Int = 0,
		dueDate: Date? = nil,
		deferUntil: Date? = nil,
		categories: [String]? = nil,
		linkedMessageId: String? = nil,
		isSomeday: Bool = false,
		createdAt: Date,
		updatedAt: Date
	) -> String {
		var lines: [String] = []
		lines.append("BEGIN:VCALENDAR")
		lines.append("VERSION:2.0")
		lines.append("PRODID:-//MagnumOpus//TaskStore//EN")
		lines.append("BEGIN:VTODO")
		lines.append("UID:\(uid)")
		lines.append("SUMMARY:\(escapeText(summary))")
		lines.append("STATUS:\(status)")
		lines.append("PRIORITY:\(priority)")
		lines.append("CREATED:\(VTODOParser.formatDate(createdAt))")
		lines.append("LAST-MODIFIED:\(VTODOParser.formatDate(updatedAt))")
		lines.append("DTSTAMP:\(VTODOParser.formatDate(updatedAt))")

		if let description {
			lines.append("DESCRIPTION:\(escapeText(description))")
		}

		if let dueDate {
			lines.append("DUE;VALUE=DATE:\(VTODOParser.formatDateOnly(dueDate))")
		}

		if let deferUntil {
			lines.append("DTSTART;VALUE=DATE:\(VTODOParser.formatDateOnly(deferUntil))")
		}

		if let categories, !categories.isEmpty {
			let escaped = categories.map { escapeText($0) }.joined(separator: ",")
			lines.append("CATEGORIES:\(escaped)")
		}

		if let linkedMessageId {
			lines.append("ATTACH:mid:\(linkedMessageId)")
		}

		if isSomeday {
			lines.append("X-MAGNUM-SOMEDAY:TRUE")
		}

		lines.append("END:VTODO")
		lines.append("END:VCALENDAR")

		// Fold lines and join with CRLF
		let folded = lines.map { foldLine($0) }
		return folded.joined(separator: "\r\n") + "\r\n"
	}

	/// Escape text per RFC 5545: backslashes first, then commas, semicolons, newlines.
	public static func escapeText(_ text: String) -> String {
		var result = text
		result = result.replacingOccurrences(of: "\\", with: "\\\\")
		result = result.replacingOccurrences(of: ",", with: "\\,")
		result = result.replacingOccurrences(of: ";", with: "\\;")
		result = result.replacingOccurrences(of: "\n", with: "\\n")
		return result
	}

	/// Fold a line at 75 octets per RFC 5545.
	/// Continuation lines start with a single space.
	public static func foldLine(_ line: String) -> String {
		let data = Array(line.utf8)
		guard data.count > 75 else { return line }

		var result = ""
		var offset = 0

		while offset < data.count {
			let chunkSize = offset == 0 ? 75 : 74 // continuation lines have a leading space
			let end = min(offset + chunkSize, data.count)
			let chunk = Array(data[offset..<end])

			if offset > 0 {
				result += "\r\n "
			}

			// Safe to convert back since we sliced valid UTF-8 at byte boundaries
			// but we need to be careful not to split multi-byte characters
			result += String(decoding: chunk, as: UTF8.self)
			offset = end
		}

		return result
	}
}
