import Foundation
import Combine
import Network

final class BindResourceMiddleware {
    private var reqId = ""

    func middleware(state: ClientState, action: ClientAction) -> AnyPublisher<ClientAction, Never> {
        switch action {
        case .bindResourceAction(.bindResource):
            let resourceName = "iPhone-" + XMLElement.randomId
            let resource = XMLElement(name: "resource", xmlns: nil, attributes: [:], content: resourceName, nodes: [])
            let bind = XMLElement(name: "bind", xmlns: "urn:ietf:params:xml:ns:xmpp-bind", attributes: [:], content: nil, nodes: [resource])
            reqId = XMLElement.randomId

            let req = XMLElement(
                name: "iq",
                xmlns: nil,
                attributes: [
                    "id": reqId,
                    "type": "set"
                ],
                content: nil,
                nodes: [bind]
            )
            return Just(ClientAction.streamAction(.streamSend(req)))
                .eraseToAnyPublisher()

        case .parserAction(.elementParsed(let element)):
            if element.name == "iq", element.attributes["id"] == reqId, element.attributes["type"] == "result" {
                if let jidStr = element
                    .nodes.first(where: { $0.name == "bind" })?
                    .nodes.first(where: { $0.name == "jid" })?
                    .content,
                let newJid = try? JID(jidStr) {
                    return Just(ClientAction.bindResourceAction(.updateJid(newJid)))
                        .eraseToAnyPublisher()
                } else {
                    return Just(ClientAction.clientError(.resourceBindingError))
                        .eraseToAnyPublisher()
                }
            } else {
                return Empty().eraseToAnyPublisher()
            }

        case .bindResourceAction(.updateJid):
            return Just(ClientAction.bindResourceAction(.resourceBound))
                .eraseToAnyPublisher()

        default:
            return Empty().eraseToAnyPublisher()
        }
    }
}
