// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cert

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/caddyserver/certmagic"
	"istio.io/istio/pilot/pkg/model"
	"k8s.io/client-go/kubernetes"
)

type Option struct {
	Namespace     string
	ServerAddress string
	Email         string
}

type Server struct {
	httpServer *http.Server
	opts       *Option
	clientSet  kubernetes.Interface
	controller *Controller
	certMgr    *CertMgr
	XDSUpdater model.XDSUpdater
}

func NewServer(clientSet kubernetes.Interface, XDSUpdater model.XDSUpdater, opts *Option) (*Server, error) {
	server := &Server{
		clientSet:  clientSet,
		opts:       opts,
		XDSUpdater: XDSUpdater,
	}
	return server, nil
}

func (s *Server) InitDefaultConfig() error {
	configMgr, _ := NewConfigMgr(s.opts.Namespace, s.clientSet)
	// init config if there is not existed
	_, err := configMgr.InitConfig(s.opts.Email)
	if err != nil {
		return err
	}
	return nil
}

func (s *Server) InitServer() error {
	configMgr, _ := NewConfigMgr(s.opts.Namespace, s.clientSet)
	// init config if there is not existed
	defaultConfig, err := configMgr.InitConfig(s.opts.Email)
	if err != nil {
		return err
	}
	// init certmgr
	certMgr, err := InitCertMgr(s.opts, s.clientSet, defaultConfig, s.XDSUpdater, configMgr) // config and start
	s.certMgr = certMgr
	// init controller
	controller, err := NewController(s.clientSet, s.opts.Namespace, certMgr, configMgr)
	s.controller = controller
	// init http server
	s.initHttpServer()
	return nil
}

func (s *Server) initHttpServer() error {
	CertLog.Infof("server init http server")
	ctx := context.Background()
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Lookit my cool website over HTTPS!")
	})
	httpServer := &http.Server{
		ReadHeaderTimeout: 5 * time.Second,
		ReadTimeout:       5 * time.Second,
		WriteTimeout:      5 * time.Second,
		IdleTimeout:       5 * time.Second,
		Addr:              s.opts.ServerAddress,
		BaseContext:       func(listener net.Listener) context.Context { return ctx },
	}
	cfg := s.certMgr.cfg
	if len(cfg.Issuers) > 0 {
		if am, ok := cfg.Issuers[0].(*certmagic.ACMEIssuer); ok {
			httpServer.Handler = am.HTTPChallengeHandler(mux)
		}
	} else {
		httpServer.Handler = mux
	}
	s.httpServer = httpServer
	return nil
}

func (s *Server) Run(stopCh <-chan struct{}) error {
	go s.controller.Run(stopCh)
	CertLog.Infof("server run")
	go func() {
		<-stopCh
		CertLog.Infof("server http server shutdown now...")
		s.httpServer.Shutdown(context.Background())
	}()
	err := s.httpServer.ListenAndServe()
	return err
}
