import Foundation

struct ServerEvent {
	let id: String?
	let event: String?
	let data: String
}

@Observable
final class SSEClient {
	private let url: URL
	private var task: Task<Void, Never>?

	var lastEvent: ServerEvent?

	init(url: URL) {
		self.url = url
	}

	func connect(onEvent: @escaping (ServerEvent) -> Void) {
		task = Task {
			do {
				var request = URLRequest(url: url)
				request.setValue("text/event-stream", forHTTPHeaderField: "Accept")

				let (bytes, _) = try await URLSession.shared.bytes(for: request)

				var currentEvent: String?
				var currentData = ""
				var currentId: String?

				for try await line in bytes.lines {
					if line.isEmpty {
						if !currentData.isEmpty {
							let event = ServerEvent(
								id: currentId,
								event: currentEvent,
								data: currentData.trimmingCharacters(in: .newlines)
							)
							self.lastEvent = event
							onEvent(event)
							currentEvent = nil
							currentData = ""
							currentId = nil
						}
					} else if line.hasPrefix("data: ") {
						currentData += String(line.dropFirst(6)) + "\n"
					} else if line.hasPrefix("event: ") {
						currentEvent = String(line.dropFirst(7))
					} else if line.hasPrefix("id: ") {
						currentId = String(line.dropFirst(4))
					}
				}
			} catch {
				if !Task.isCancelled {
					try? await Task.sleep(for: .seconds(5))
					connect(onEvent: onEvent)
				}
			}
		}
	}

	func disconnect() {
		task?.cancel()
		task = nil
	}
}
