## tests/test_nexter_comm.nim
## Tests for Nippel-Nexter Communication

import std/[unittest, json, times, tables, asyncdispatch, options, strutils]
import ../src/nimpak/nexter_comm
import ../src/nimpak/utils/resultutils

suite "Nippel-Nexter Communication":
  
  test "Message ID generation":
    let id1 = newMessageId()
    let id2 = newMessageId()
    check id1.len > 0
    check id2.len > 0
    check id1 != id2
  
  test "Message header creation":
    let header = newMessageHeader(ServiceRequest, "sender", "recipient", High)
    check header.messageType == ServiceRequest
    check header.sender == "sender"
    check header.recipient == "recipient"
    check header.priority == High
    check header.messageId.len > 0
    check header.ttl == 300
  
  test "Complete message creation":
    let content = %*{"service": "test", "data": 42}
    let msg = newCommMessage(ServiceRequest, "test-sender", content, "test-recipient", Critical)
    check msg.header.messageType == ServiceRequest
    check msg.header.sender == "test-sender"
    check msg.header.recipient == "test-recipient"
    check msg.header.priority == Critical
    check msg.body.content == content
    check msg.signature == ""
  
  test "Message JSON serialization":
    let content = %*{"test": "serialization"}
    let msg = newCommMessage(ServiceRequest, "sender", content)
    let json = msg.toJson()
    check json.hasKey("header")
    check json.hasKey("body")
    check json.hasKey("signature")
    check json["header"]["messageType"].getStr() == "ServiceRequest"
    check json["header"]["sender"].getStr() == "sender"
    check json["body"]["content"] == content
  
  test "Message JSON deserialization":
    let originalContent = %*{"test": "deserialization", "number": 123}
    let originalMsg = newCommMessage(ServiceResponse, "test-sender", originalContent)
    let json = originalMsg.toJson()
    let deserializedResult = fromJson(json)
    check deserializedResult.isOk
    let deserializedMsg = deserializedResult.value
    check deserializedMsg.header.messageType == ServiceResponse
    check deserializedMsg.header.sender == "test-sender"
    check deserializedMsg.body.content == originalContent
  
  test "Communication manager creation":
    let manager = newCommManager("test-nippel")
    check manager.nippelName == "test-nippel"
    check manager.knownNexters.len == 0
    check manager.authContext.isNone
    check manager.messageHandlers.len == 0
    check manager.discoveryInterval == 30
  
  test "Message validation - valid message":
    let msg = newCommMessage(ServiceRequest, "sender", %*{"test": true})
    let result = validateMessage(msg)
    check result.isOk
    check result.value == true
  
  test "Message validation - empty sender":
    var msg = newCommMessage(ServiceRequest, "", %*{"test": true})
    let result = validateMessage(msg)
    check result.isErr
    check result.error.contains("Sender cannot be empty")
  
  test "Message expiration check":
    var msg = newCommMessage(ServiceRequest, "sender", %*{"test": true})
    check not isMessageExpired(msg)
    msg.header.timestamp = now() - 400.seconds
    check isMessageExpired(msg)
  
  test "Service finding - not found":
    let manager = newCommManager("test-nippel")
    let service = manager.findService(FileSystem, "test-service")
    check service.isNone
  
  test "Authentication context creation":
    let auth = createAuthContext(Token, "test-token-123", "", 1800)
    check auth.authMethod == Token
    check auth.token == "test-token-123"
    check auth.certificate == ""
    check auth.permissions.len == 0
    check isAuthValid(auth)
  
  test "Message authentication - no auth required":
    let manager = newCommManager("test-nippel")
    var msg = newCommMessage(ServiceRequest, "sender", %*{"test": true})
    let result = manager.authenticateMessage(msg)
    check result.isOk
    check result.value == true
  
  test "Message authentication - with token":
    let manager = newCommManager("test-nippel")
    let auth = createAuthContext(Token, "secret-token")
    manager.authContext = some(auth)
    var msg = newCommMessage(ServiceRequest, "sender", %*{"test": true})
    let result = manager.authenticateMessage(msg)
    check result.isOk
    check msg.signature == "secret-token"
  
  test "Message routing - with handler":
    let manager = newCommManager("test-nippel")
    
    proc echoHandler(msg: CommMessage): Future[CommMessage] {.async.} =
      return newCommMessage(ServiceResponse, "handler", %*{"echo": msg.body.content})
    
    manager.registerMessageHandler(ServiceRequest, echoHandler)
    
    proc testRouting() {.async.} =
      let msg = newCommMessage(ServiceRequest, "sender", %*{"data": "test"})
      let result = await manager.routeMessage(msg)
      check result.isOk
      let response = result.value
      check response.header.messageType == ServiceResponse
      check response.body.content["echo"]["data"].getStr() == "test"
    
    waitFor testRouting()
  
  test "Error message creation":
    let originalMsg = newCommMessage(ServiceRequest, "client", %*{"request": "data"}, "server")
    let errorMsg = createErrorMessage(originalMsg, ServiceNotFound, "The requested service was not found")
    check errorMsg.header.messageType == ErrorMessage
    check errorMsg.header.sender == "server"
    check errorMsg.header.recipient == "client"
    check errorMsg.header.priority == High
    check errorMsg.header.correlationId == originalMsg.header.messageId
    let errorInfo = errorMsg.body.content["error"]
    check errorInfo["code"].getInt() == ord(ServiceNotFound)
    check errorInfo["message"].getStr() == "The requested service was not found"
  
  test "Complete message flow":
    let manager = newCommManager("test-nippel")
    
    proc serviceHandler(msg: CommMessage): Future[CommMessage] {.async.} =
      let request = msg.body.content
      let response = %*{
        "status": "success",
        "result": "Processed: " & request["data"].getStr()
      }
      return newCommMessage(ServiceResponse, "service", response)
    
    manager.registerMessageHandler(ServiceRequest, serviceHandler)
    
    proc testFlow() {.async.} =
      let msg = newCommMessage(ServiceRequest, "client", %*{"data": "test-data"})
      let result = await manager.sendMessage(msg)
      check result.isOk
      let response = result.value
      check response.header.messageType == ServiceResponse
      check response.body.content["status"].getStr() == "success"
      check response.body.content["result"].getStr().contains("Processed: test-data")
    
    waitFor testFlow()

echo "✓ All Nippel-Nexter Communication tests completed"
