// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package server

import (
	
	
	
	

	
	
)

const (
	// See: https://tools.ietf.org/html/rfc5766#section-6.2 defines 3600 seconds recommendation.
	maximumAllocationLifetime = time.Hour
)

func buildAndSend( net.PacketConn,  net.Addr,  ...stun.Setter) error {
	,  := stun.Build(...)
	if  != nil {
		return 
	}
	_,  = .WriteTo(.Raw, )
	if errors.Is(, net.ErrClosed) {
		return nil
	}

	return 
}

// Send a STUN packet and return the original error to the caller.
func buildAndSendErr( net.PacketConn,  net.Addr,  error,  ...stun.Setter) error {
	if  := buildAndSend(, , ...);  != nil {
		 = fmt.Errorf("%w %v %v", errFailedToSendError, , ) //nolint:errorlint
	}

	return 
}

func buildMsg(
	 [stun.TransactionIDSize]byte,
	 stun.MessageType,
	 ...stun.Setter,
) []stun.Setter {
	return append([]stun.Setter{&stun.Message{TransactionID: }, }, ...)
}

func authenticateRequest( Request,  *stun.Message,  stun.Method) (
	stun.MessageIntegrity,
	bool,
	error,
) {
	 := func( stun.ErrorCode) (stun.MessageIntegrity, bool, error) {
		,  := .NonceHash.Generate()
		if  != nil {
			return nil, false, 
		}

		return nil, false, buildAndSend(.Conn, .SrcAddr, buildMsg(.TransactionID,
			stun.NewType(, stun.ClassErrorResponse),
			&stun.ErrorCodeAttribute{Code: },
			stun.NewNonce(),
			stun.NewRealm(.Realm),
		)...)
	}

	if !.Contains(stun.AttrMessageIntegrity) {
		return (stun.CodeUnauthorized)
	}

	 := &stun.Nonce{}
	 := &stun.Username{}
	 := &stun.Realm{}
	 := buildMsg(
		.TransactionID,
		stun.NewType(, stun.ClassErrorResponse),
		&stun.ErrorCodeAttribute{Code: stun.CodeBadRequest},
	)

	// No Auth handler is set, server is running in STUN only mode
	// Respond with 400 so clients don't retry.
	if .AuthHandler == nil {
		 := buildAndSend(.Conn, .SrcAddr, ...)

		return nil, false, 
	}

	if  := .GetFrom();  != nil {
		return nil, false, buildAndSendErr(.Conn, .SrcAddr, , ...)
	}

	// Assert Nonce is signed and is not expired.
	if  := .NonceHash.Validate(.String());  != nil {
		return (stun.CodeStaleNonce)
	}

	if  := .GetFrom();  != nil {
		return nil, false, buildAndSendErr(.Conn, .SrcAddr, , ...)
	} else if  := .GetFrom();  != nil {
		return nil, false, buildAndSendErr(.Conn, .SrcAddr, , ...)
	}

	,  := .AuthHandler(.String(), .String(), .SrcAddr)
	if ! {
		return nil, false, buildAndSendErr(
			.Conn,
			.SrcAddr,
			fmt.Errorf("%w %s", errNoSuchUser, .String()),
			...,
		)
	}

	if  := stun.MessageIntegrity().Check();  != nil {
		return nil, false, buildAndSendErr(.Conn, .SrcAddr, , ...)
	}

	return stun.MessageIntegrity(), true, nil
}

func allocationLifeTime( *stun.Message) time.Duration {
	 := proto.DefaultLifetime

	var  proto.Lifetime
	if  := .GetFrom();  == nil {
		if .Duration < maximumAllocationLifetime {
			 = .Duration
		}
	}

	return 
}