package main

import (
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/elazarl/goproxy"
	"github.com/spf13/cobra"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/log"
)

var (
	proxyAddr    string
	internalAddr string
	listenAddr   string
)

func newRootCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:     "devops-server-migrate-proxy",
		Version: "v0.0.0",
		Run:     excute,
	}
	cmd.PersistentFlags().StringVar(&proxyAddr, "proxy-addr", "", "the address of the proxy server")
	cmd.PersistentFlags().StringVar(&internalAddr, "internal-proxy-addr", "", "the internal address of the proxy server")
	cmd.PersistentFlags().StringVar(&listenAddr, "listen-addr", ":8088", "the address of the proxy server")
	cmd.MarkFlagsRequiredTogether("proxy-addr")

	return cmd
}

func excute(cmd *cobra.Command, args []string) {
	log.InitLogger(&config.LogConfig{})

	proxy := goproxy.NewProxyHttpServer()

	proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		req.URL.Scheme = "https"
		proxy.ServeHTTP(w, req)
	})

	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			r.URL.Scheme = "https"
			r.URL.Host = proxyAddr
			if strings.HasPrefix(r.URL.Path, "/api/v1/devops-internal") {
				r.URL.Scheme = "http"
				r.URL.Host = internalAddr
			}
			r.Header.Set("Access-Control-Allow-Origin", "*")
			r.Header.Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
			r.Header.Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

			log.Logger.Info("proxy request", "proxy-url", r.URL.String(), "method", r.Method)
			return r, nil
		},
	)
	proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
		logger := log.Logger
		if resp != nil {
			logger = logger.WithValues("status", resp.Status)
		}
		if ctx != nil {
			logger = logger.WithValues("url", ctx.Req.URL.String()).WithValues("error", ctx.Error)
		}

		logger.Info("proxy response")
		return resp
	})

	log.Logger.Info("server is running", "listen-addr", listenAddr, "proxy-addr", proxyAddr)

	server := &http.Server{
		Addr:              listenAddr,
		Handler:           proxy,
		ReadHeaderTimeout: 3 * time.Second,
	}
	err := server.ListenAndServe()
	if err != nil {
		log.Logger.Error(err, "failed to start server")
		os.Exit(1)
	}

	log.Logger.Info("exit")
}

func main() {
	cmd := newRootCmd()
	cmd.Execute()
}
