class_name NetworkSaleProtocol
extends RefCounted

const CAPABILITY: StringName = &"sale_v1"
const RELIABLE_CHANNEL: int = NetworkProtocol.SALE_RELIABLE_CHANNEL
const MAX_ID_LENGTH: int = 96
const MAX_CATCH_ID_LENGTH: int = 160
const MAX_CATCHES_PER_REQUEST: int = 64
const MAX_MESSAGE_LENGTH: int = 160

enum Rejection {
	NONE,
	MALFORMED,
	STALE_SESSION,
	UNAUTHENTICATED,
	ALREADY_PENDING,
	BUYER_UNAVAILABLE,
	TOO_FAR,
	INVALID_CATCH,
	FAVORITED,
	UNSUPPORTED,
}


static func validate_request(data: Variant) -> String:
	if typeof(data) != TYPE_DICTIONARY:
		return "Sale could not be completed."
	var payload: Dictionary = data
	for key: String in ["request_id", "session_id", "buyer_id", "catches"]:
		if not payload.has(key):
			return "Sale could not be completed."
	if (
		typeof(payload["request_id"]) != TYPE_STRING
		or typeof(payload["session_id"]) != TYPE_STRING
		or typeof(payload["buyer_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]
		or typeof(payload["catches"]) != TYPE_ARRAY
	):
		return "Sale could not be completed."
	var request_id: String = payload["request_id"]
	var session_id: String = payload["session_id"]
	var buyer_id: String = str(payload["buyer_id"])
	var catches: Array = payload["catches"]
	if (
		request_id.is_empty()
		or request_id.length() > MAX_ID_LENGTH
		or session_id.is_empty()
		or session_id.length() > MAX_ID_LENGTH
		or buyer_id.is_empty()
		or buyer_id.length() > MAX_ID_LENGTH
		or catches.is_empty()
		or catches.size() > MAX_CATCHES_PER_REQUEST
	):
		return "Sale could not be completed."
	var seen: Dictionary[String, bool] = {}
	for value: Variant in catches:
		if typeof(value) != TYPE_DICTIONARY:
			return "Sale could not be completed."
		var evidence: Dictionary = value
		for key: String in [
			"catch_id", "fish_id", "weight_lb", "display_scale",
			"quality", "sale_value", "is_favorited",
		]:
			if not evidence.has(key):
				return "Sale could not be completed."
		var catch_id: String = str(evidence["catch_id"])
		if (
			catch_id.is_empty()
			or catch_id.length() > MAX_CATCH_ID_LENGTH
			or seen.has(catch_id)
			or typeof(evidence["fish_id"]) not in [
				TYPE_STRING, TYPE_STRING_NAME
			]
			or typeof(evidence["is_favorited"]) != TYPE_BOOL
		):
			return "Sale could not be completed."
		seen[catch_id] = true
	return ""


static func validate_result(data: Variant) -> bool:
	if typeof(data) != TYPE_DICTIONARY:
		return false
	var payload: Dictionary = data
	if not (
		typeof(payload.get("result_id")) == TYPE_STRING
		and not str(payload["result_id"]).is_empty()
		and str(payload["result_id"]).length() <= MAX_ID_LENGTH
		and typeof(payload.get("request_id")) == TYPE_STRING
		and not str(payload["request_id"]).is_empty()
		and str(payload["request_id"]).length() <= MAX_ID_LENGTH
		and typeof(payload.get("session_id")) == TYPE_STRING
		and typeof(payload.get("target_peer_id")) == TYPE_INT
		and typeof(payload.get("accepted")) == TYPE_BOOL
		and typeof(payload.get("catch_ids")) == TYPE_ARRAY
		and payload["catch_ids"].size() <= MAX_CATCHES_PER_REQUEST
		and typeof(payload.get("payout")) == TYPE_INT
		and int(payload["payout"]) >= 0
		and typeof(payload.get("base_value")) == TYPE_INT
		and int(payload["base_value"]) >= 0
		and typeof(payload.get("message")) == TYPE_STRING
		and str(payload["message"]).length() <= MAX_MESSAGE_LENGTH
	):
		return false
	if payload.has("buyer_id") and (
		typeof(payload["buyer_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]
		or str(payload["buyer_id"]).is_empty()
		or str(payload["buyer_id"]).length() > MAX_ID_LENGTH
	):
		return false
	var seen: Dictionary[String, bool] = {}
	for value: Variant in payload["catch_ids"]:
		if typeof(value) not in [TYPE_STRING, TYPE_STRING_NAME]:
			return false
		var catch_id: String = str(value)
		if (
			catch_id.is_empty()
			or catch_id.length() > MAX_CATCH_ID_LENGTH
			or seen.has(catch_id)
		):
			return false
		seen[catch_id] = true
	return true
