// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build linux && go1.25 && !without_badtls

package ktls

import (
	"fmt"
	"net"
	"time"
)

func (c *Conn) Close() error {
	if !c.kernelTx {
		return c.Conn.Close()
	}

	// Interlock with Conn.Write above.
	var x int32
	for {
		x = c.rawConn.ActiveCall.Load()
		if x&1 != 0 {
			return net.ErrClosed
		}
		if c.rawConn.ActiveCall.CompareAndSwap(x, x|1) {
			break
		}
	}
	if x != 0 {
		// io.Writer and io.Closer should not be used concurrently.
		// If Close is called while a Write is currently in-flight,
		// interpret that as a sign that this Close is really just
		// being used to break the Write and/or clean up resources and
		// avoid sending the alertCloseNotify, which may block
		// waiting on handshakeMutex or the c.out mutex.
		return c.conn.Close()
	}

	var alertErr error
	if c.rawConn.IsHandshakeComplete.Load() {
		if err := c.closeNotify(); err != nil {
			alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err)
		}
	}

	if err := c.conn.Close(); err != nil {
		return err
	}
	return alertErr
}

func (c *Conn) closeNotify() error {
	c.rawConn.Out.Lock()
	defer c.rawConn.Out.Unlock()

	if !*c.rawConn.CloseNotifySent {
		// Set a Write Deadline to prevent possibly blocking forever.
		c.SetWriteDeadline(time.Now().Add(time.Second * 5))
		*c.rawConn.CloseNotifyErr = c.sendAlertLocked(alertCloseNotify)
		*c.rawConn.CloseNotifySent = true
		// Any subsequent writes will fail.
		c.SetWriteDeadline(time.Now())
	}
	return *c.rawConn.CloseNotifyErr
}
