package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"gopkg.in/yaml.v2"
)

// Backend结构体
var Backends []*Backend

// 设置跨域请求
func SetCors(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
	w.Header().Set("Access-Control-Allow-Headers", "*")
	w.Header().Set("Access-Control-Expose-Headers", "*")
	w.Header().Set("Access-Control-Allow-Credentials", "true")

	// 如果请求方法为 OPTIONS,直接返回 200 状态码
	if r.Method == "OPTIONS" {
		w.WriteHeader(http.StatusOK)
		return
	}
}

// 存储路径的并发安全的Map
var pathdict sync.Map = sync.Map{}

func main() {
	log.SetFlags(log.Llongfile)

	// 将静态资源路径存储到pathdict
	pathdict.Store("/css", true)
	pathdict.Store("/fonts", true)
	pathdict.Store("/img", true)
	pathdict.Store("/js", true)
	pathdict.Store("/svg", true)
	pathdict.Store("/favicon.ico", true)

	rootDir := "../server" // 更改为你的根目录
	vueBuild := "/opt/fusenpack-vue-created"
	apiURL, err := url.Parse("http://localhost:9900")
	if err != nil {
		panic(err)
	}

	mux := http.NewServeMux()

	// 获取并解析服务信息
	results := GetZeroInfo(rootDir)

	var allRoutes map[string]bool = make(map[string]bool)
	for _, result := range results {
		fmt.Printf("FolderName: %s, Host: %s, Port: %d, PrefixRoute: %v\n", result.FolderName, result.Host, result.Port, result.PrefixRoute)
		var routes []string
		for k := range result.PrefixRoute {
			routes = append(routes, k)
			allRoutes[k] = true
		}

		// 根据获取的服务信息创建后端服务
		Backends = append(Backends,
			NewBackend(mux,
				fmt.Sprintf("http://%s:%d", result.Host, result.Port),
				routes...))
	}

	// 定义用于服务Vue dist文件夹的静态文件服务器
	fs := http.FileServer(http.Dir(vueBuild))
	indexHtmlPath := vueBuild + "/index.html"
	mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		err := r.ParseMultipartForm(100 << 20)
		if err != nil {
			log.Println(err)
		}

		if strings.HasPrefix(r.URL.Path, "/api/") {
			// 对/api开头的请求进行反向代理
			proxy := httputil.NewSingleHostReverseProxy(apiURL)
			proxy.ServeHTTP(w, r)
			return

		} else {
			// 根据请求路径判断是服务静态文件或者是返回index.html
			idx := strings.Index(r.URL.Path[1:], "/")
			var prefix string
			if idx != -1 {
				prefix = r.URL.Path[:idx+1]
			} else {
				prefix = r.URL.Path
			}

			if _, ok := pathdict.Load(prefix); ok {
				fs.ServeHTTP(w, r)
			} else {
				http.ServeFile(w, r, indexHtmlPath)
			}

		}
	}))

	ServerAddress := ":9900"
	log.Println("listen on ", ServerAddress)

	keydata, err := os.ReadFile("/opt/server.fusen.3718.cn.key")
	if err != nil {
		panic(err)
	}

	pemdata, err := os.ReadFile("/opt/server.fusen.3718.cn.pem")
	if err != nil {
		panic(err)
	}

	cert, err := tls.X509KeyPair(pemdata, keydata)
	if err != nil {
		panic(err)
	}

	tlscfg := &tls.Config{
		Certificates: []tls.Certificate{cert},
		MinVersion:   tls.VersionTLS12,
		MaxVersion:   tls.VersionTLS13,
	}
	serv := http.Server{
		Addr:      ServerAddress,
		Handler:   mux,
		TLSConfig: tlscfg,
	}

	log.Fatal(serv.ListenAndServeTLS("", ""))
}

// 后端服务的类型
type Backend struct {
	HttpAddress string
	Client      *http.Client
	Handler     http.HandlerFunc
	Dialer      *websocket.Dialer
}

func NewBackend(mux *http.ServeMux, httpAddress string, muxPaths ...string) *Backend {
	// 如果路径最后没有以'/'结尾,则添加'/'
	for i, muxPath := range muxPaths {
		if muxPath[len(muxPath)-1] != '/' {
			muxPath = muxPath + "/"
			muxPaths[i] = muxPath
		}
	}

	// 创建HTTP客户端,设置相关的超时参数和连接数限制
	client := &http.Client{
		Transport: &http.Transport{
			DialContext: (&net.Dialer{
				Timeout:   300 * time.Second,
				KeepAlive: 60 * time.Second,
			}).DialContext,
			ForceAttemptHTTP2:     true,
			MaxIdleConns:          100,
			MaxIdleConnsPerHost:   100,
			IdleConnTimeout:       300 * time.Second,
			TLSHandshakeTimeout:   300 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}

	// 创建后端服务对象,包含地址和客户端
	backend := &Backend{
		HttpAddress: httpAddress,
		Client:      client,
	}

	// 创建处理请求的函数
	handleRequest := func(w http.ResponseWriter, r *http.Request) {

		if websocket.IsWebSocketUpgrade(r) {
			//todo: 建立websocket的代理

			target := url.URL{Scheme: "ws", Host: strings.Split(backend.HttpAddress, "//")[1], Path: r.URL.Path}

			var transfer = func(src, dest *websocket.Conn) {
				for {
					mType, msg, err := src.ReadMessage()
					if err != nil {
						log.Println(err)
						break
					}

					err = dest.WriteMessage(mType, msg)
					if err != nil {
						log.Println(err)
						break
					}
				}

				src.Close()
				dest.Close()
			}

			header := r.Header.Clone()
			// log.Println(target.String())
			header.Del("Sec-Websocket-Extensions")
			header.Del("Upgrade")
			header.Del("Sec-Websocket-Key")
			header.Del("Sec-Websocket-Version")
			header.Del("Connection")

			// header.Del("Origin")
			proxyConn, _, err := backend.Dialer.Dial(target.String(), header)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			// defer proxyConn.Close()

			upgrader := websocket.Upgrader{
				CheckOrigin: func(r *http.Request) bool { return true },
			}
			conn, err := upgrader.Upgrade(w, r, nil)
			if err != nil {
				return
			}
			// defer conn.Close()

			go transfer(proxyConn, conn)
			// go transfer(conn, proxyConn)
			return
		}

		// 解析目标URL,包含了查询参数
		targetURL, err := url.Parse(httpAddress + r.URL.String())
		if err != nil {
			http.Error(w, "Error parsing target URL", http.StatusInternalServerError)
			return
		}

		// 创建新的请求
		proxyReq, err := http.NewRequest(r.Method, targetURL.String(), r.Body)
		if err != nil {
			http.Error(w, "Error creating proxy request", http.StatusInternalServerError)
			return
		}

		// 复制原始请求的 Header
		for key, values := range r.Header {
			for _, value := range values {
				proxyReq.Header.Add(key, value)
			}
		}

		// 设置 Content-Length 和 Content-Type
		proxyReq.ContentLength = r.ContentLength
		proxyReq.Header.Set("Content-Type", r.Header.Get("Content-Type"))

		// 发送请求
		resp, err := backend.Client.Do(proxyReq)
		if err != nil {
			http.Error(w, "Error sending proxy request", http.StatusInternalServerError)
			return
		}
		defer resp.Body.Close()

		// 复制目标服务器的响应 Header
		for key, values := range resp.Header {
			for _, value := range values {
				w.Header().Add(key, value)
			}
		}

		// 转发目标服务器的响应状态码和主体
		w.WriteHeader(resp.StatusCode)
		_, err = io.Copy(w, resp.Body)
		if err != nil {
			http.Error(w, "Error copying proxy response", http.StatusInternalServerError)
			return
		}
	}

	// 为每个路径注册处理函数
	for _, muxPath := range muxPaths {
		mux.HandleFunc(muxPath, handleRequest)
	}

	// 返回后端服务对象
	return backend
}

// get_zero_info.go

// Config 结构体用于解析yaml配置文件
type Config struct {
	Host string `yaml:"Host"`
	Port int    `yaml:"Port"`
}

// Result 结构体用于存储解析结果
type Result struct {
	FolderName  string
	Host        string
	Port        int
	PrefixRoute map[string]bool
}

// GetZeroInfo 遍历指定目录,并解析相关信息
func GetZeroInfo(rootDir string) (results []*Result) {
	entries, err := ioutil.ReadDir(rootDir)
	if err != nil {
		log.Fatal(err)
	}

	for _, entry := range entries {
		// 只处理目录类型
		if entry.IsDir() {
			result, err := findFoldersAndExtractInfo(rootDir, entry)
			if err != nil {
				log.Fatal(err)
			}

			results = append(results, result)
		}
	}

	return
}

// findFoldersAndExtractInfo 查找目录并提取信息
func findFoldersAndExtractInfo(rootDir string, entry os.FileInfo) (*Result, error) {
	var result *Result

	folderName := entry.Name()
	path := filepath.Join(rootDir, folderName)

	err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 跳过非目录类型
		if !info.IsDir() {
			return nil
		}

		relPath, err := filepath.Rel(path, path)
		if err != nil {
			return err
		}

		// 跳过非当前目录的子目录
		if strings.Contains(relPath, string(os.PathSeparator)) {
			return filepath.SkipDir
		}

		// 读取配置文件
		configPath := filepath.Join(path, "etc", folderName+".yaml")
		routesPath := filepath.Join(path, "internal", "handler", "routes.go")

		configContent, err := ioutil.ReadFile(configPath)
		if err != nil {
			return err
		}

		var config Config
		err = yaml.Unmarshal(configContent, &config)
		if err != nil {
			return err
		}

		// 读取路由文件
		routesContent, err := ioutil.ReadFile(routesPath)
		if err != nil {
			return err
		}

		PrefixRoute := extractPrefixRouteValues(string(routesContent))

		// 构建结果
		result = &Result{
			FolderName:  folderName,
			Host:        config.Host,
			Port:        config.Port,
			PrefixRoute: PrefixRoute,
		}

		return filepath.SkipDir
	})

	if err != nil {
		return nil, err
	}

	return result, nil
}

// extractPrefixRouteValues 提取路由前缀
func extractPrefixRouteValues(content string) map[string]bool {
	lines := strings.Split(content, "\n")
	var prefixPath map[string]bool = make(map[string]bool)

	for _, line := range lines {
		// 查找包含 "Path:" 的行
		if strings.Contains(line, "Path:") {
			path := strings.TrimSpace(strings.TrimPrefix(line, "Path:"))
			paths := strings.Split(strings.Trim(path, `"`), "/")
			path1 := "/" + paths[1] + "/" + paths[2]

			if _, ok := prefixPath[path1]; !ok {
				prefixPath[path1] = true
			}
		}
	}

	return prefixPath
}