// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0

package http

import (
	"context"
	"crypto/tls"
	"fmt"
	"log/slog"
	"net/http"

	"github.com/absmach/magistrala/pkg/server"
)

const (
	httpProtocol  = "http"
	httpsProtocol = "https"
)

type httpServer struct {
	server.BaseServer
	server *http.Server
}

var _ server.Server = (*httpServer)(nil)

func NewServer(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, handler http.Handler, logger *slog.Logger) server.Server {
	baseServer := server.NewBaseServer(ctx, cancel, name, config, logger)
	hserver := &http.Server{
		Addr:              baseServer.Address,
		Handler:           handler,
		ReadTimeout:       config.ReadTimeout,
		WriteTimeout:      config.WriteTimeout,
		ReadHeaderTimeout: config.ReadHeaderTimeout,
		IdleTimeout:       config.IdleTimeout,
		MaxHeaderBytes:    config.MaxHeaderBytes,
	}

	return &httpServer{
		BaseServer: baseServer,
		server:     hserver,
	}
}

func (s *httpServer) Start() error {
	errCh := make(chan error)
	s.Protocol = httpProtocol
	switch {
	case s.Config.CertFile != "" || s.Config.KeyFile != "":
		certs, err := server.LoadX509KeyPair(s.Config.CertFile, s.Config.KeyFile)
		if err != nil {
			return err
		}

		if s.server.TLSConfig == nil {
			s.server.TLSConfig = &tls.Config{}
		}

		tlsConf := s.server.TLSConfig.Clone()
		tlsConf.Certificates = append(tlsConf.Certificates, certs)
		s.server.TLSConfig = tlsConf
		s.Protocol = httpsProtocol

		s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS", s.Name, s.Protocol, s.Address))
		go func() {
			errCh <- s.server.ListenAndServeTLS("", "")
		}()
	default:
		s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s without TLS", s.Name, s.Protocol, s.Address))
		go func() {
			errCh <- s.server.ListenAndServe()
		}()
	}

	select {
	case <-s.Ctx.Done():
		return s.Stop()
	case err := <-errCh:
		return err
	}
}

func (s *httpServer) Stop() error {
	defer s.Cancel()
	ctx, cancel := context.WithTimeout(context.Background(), server.StopWaitTime)
	defer cancel()
	if err := s.server.Shutdown(ctx); err != nil {
		s.Logger.Error(fmt.Sprintf("%s service %s server error occurred during shutdown at %s: %s", s.Name, s.Protocol, s.Address, err))
		return fmt.Errorf("%s service %s server error occurred during shutdown at %s: %w", s.Name, s.Protocol, s.Address, err)
	}
	s.Logger.Info(fmt.Sprintf("%s %s service shutdown of http at %s", s.Name, s.Protocol, s.Address))
	return nil
}
