// Copyright 2026 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 go1.27 && !http2legacy

// Server wrapping a net/http.Server.

package http2

import (
	"context"
	"errors"
	"net"
	"net/http"
	"sync"
	"time"
)

type serverInternalState struct {
	s1            *http.Server
	initOnce      sync.Once
	serveConnFunc func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte)
}

func configureServer(s *http.Server, conf *Server) error {
	if s == nil {
		panic("nil *http.Server")
	}
	if conf == nil {
		conf = new(Server)
	}
	if conf.state != nil {
		// This isn't a panic in the pre-wrapping implementation,
		// but calling ConfigureServer twice with the same http2.Server
		// overwrites internal state on the server.
		// Make the error explicit and early here.
		panic("ConfigureServer may be called only once per Server")
	}
	if h1, h2 := s, conf; h2.IdleTimeout == 0 {
		if h1.IdleTimeout != 0 {
			h2.IdleTimeout = h1.IdleTimeout
		} else {
			h2.IdleTimeout = h1.ReadTimeout
		}
	}
	conf.state = &serverInternalState{
		s1: s,
	}
	sconfig := &serverConfig{s: conf}
	if err := s.Serve(sconfig); err != nil || sconfig.serveConnFunc == nil {
		panic("http2: net/http does not support this version of x/net/http2")
	}
	conf.state.serveConnFunc = sconfig.serveConnFunc
	return nil
}

type serverConfig struct {
	s             *Server
	serveConnFunc func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte)
}

func (*serverConfig) Accept() (net.Conn, error) {
	return nil, errors.New("unexpected call to Accept")
}
func (*serverConfig) Close() error {
	return nil
}
func (*serverConfig) Addr() net.Addr {
	return nil
}

func (s *serverConfig) ServeConnFunc(f func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte)) {
	s.serveConnFunc = f
}

func (s *serverConfig) HTTP2Config() http.HTTP2Config {
	return http.HTTP2Config{
		MaxConcurrentStreams:          int(s.s.MaxConcurrentStreams),
		MaxDecoderHeaderTableSize:     int(s.s.MaxDecoderHeaderTableSize),
		MaxEncoderHeaderTableSize:     int(s.s.MaxEncoderHeaderTableSize),
		MaxReadFrameSize:              int(s.s.MaxReadFrameSize),
		PermitProhibitedCipherSuites:  s.s.PermitProhibitedCipherSuites,
		MaxReceiveBufferPerConnection: int(s.s.MaxUploadBufferPerConnection),
		MaxReceiveBufferPerStream:     int(s.s.MaxUploadBufferPerStream),
		SendPingTimeout:               s.s.ReadIdleTimeout,
		PingTimeout:                   s.s.PingTimeout,
		WriteByteTimeout:              s.s.WriteByteTimeout,
		CountError:                    s.s.CountError,
	}
}

func (s *serverConfig) IdleTimeout() time.Duration {
	return s.s.IdleTimeout
}

type serverConn struct{}

func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, _ func(*serverConn)) {
	var serveConnFunc func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte)
	switch {
	case opts.BaseConfig != nil:
		// The user has provided us with an http.Server to take configuration from.
		//
		// We can't send our request to opts.BaseConfig, because an http.Server can
		// only be associated with a single http2.Server and the user might
		// use this one with several http.Servers.
		//
		// We can't send our request to s.state.s1, because it doesn't contain
		// the right configuration.
		//
		// So create a one-off copy of opts.BaseConfig and use it.
		h1 := &http.Server{
			TLSConfig:         opts.BaseConfig.TLSConfig,
			ReadTimeout:       opts.BaseConfig.ReadTimeout,
			ReadHeaderTimeout: opts.BaseConfig.ReadHeaderTimeout,
			WriteTimeout:      opts.BaseConfig.WriteTimeout,
			IdleTimeout:       opts.BaseConfig.IdleTimeout,
			MaxHeaderBytes:    opts.BaseConfig.MaxHeaderBytes,
			ConnState:         opts.BaseConfig.ConnState,
			ErrorLog:          opts.BaseConfig.ErrorLog,
			BaseContext:       opts.BaseConfig.BaseContext,
			ConnContext:       opts.BaseConfig.ConnContext,
			HTTP2:             opts.BaseConfig.HTTP2,
		}
		sconfig := &serverConfig{s: s}
		if err := h1.Serve(sconfig); err != nil || sconfig.serveConnFunc == nil {
			panic("http2: net/http does not support this version of x/net/http2")
		}
		serveConnFunc = sconfig.serveConnFunc
	case s.state != nil:
		serveConnFunc = s.state.serveConnFunc
	default:
		// Strange-but-true: Server has no concurrency-safe way to initialize
		// its internal state, so historically ServeConn just doesn't use any
		// persistent state if you don't call ConfigureServer first.
		//
		// If ConfigureServer hasn't been called, create a one-off http.Server
		// for the connection, since we don't have any way to keep one around for reuse.
		h1 := &http.Server{}
		sconfig := &serverConfig{s: s}
		if err := h1.Serve(sconfig); err != nil || sconfig.serveConnFunc == nil {
			panic("http2: net/http does not support this version of x/net/http2")
		}
		serveConnFunc = sconfig.serveConnFunc
	}

	ctx, cancel := serverConnBaseContext(c, opts)
	defer cancel()
	serveConnFunc(ctx, c, opts.handler(), opts.SawClientPreface, opts.UpgradeRequest, opts.Settings)

}

// FrameWriteRequest is a request to write a frame.
//
// Deprecated: User-provided write schedulers are deprecated.
type FrameWriteRequest struct {
	// Ideally we'd define this in writesched_common.go,
	// to avoid duplicating an exported symbol across two files,
	// but the changes required to make this work are fairly large.
}

func (wr FrameWriteRequest) StreamID() uint32 {
	return 0
}

func (wr FrameWriteRequest) DataSize() int {
	return 0
}

func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteRequest, int) {
	return FrameWriteRequest{}, FrameWriteRequest{}, 0
}

func (wr FrameWriteRequest) String() string {
	return ""
}

// NewPriorityWriteScheduler is deprecated.
//
// Deprecated: User-provided write schedulers are deprecated.
func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler {
	return unsupportedWriteScheduler{}
}

// NewRandomWriteScheduler is deprecated.
//
// Deprecated: User-provided write schedulers are deprecated.
func NewRandomWriteScheduler() WriteScheduler {
	return unsupportedWriteScheduler{}
}

type unsupportedWriteScheduler struct{}

func (unsupportedWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) {}
func (unsupportedWriteScheduler) CloseStream(streamID uint32)                           {}
func (unsupportedWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam)  {}
func (unsupportedWriteScheduler) Push(wr FrameWriteRequest)                             {}
func (unsupportedWriteScheduler) Pop() (wr FrameWriteRequest, ok bool) {
	return FrameWriteRequest{}, false
}
