package main

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

	"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() {
	// 将静态资源路径存储到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) {
		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)
	log.Fatal(http.ListenAndServe(ServerAddress, mux))
}

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

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:   60 * time.Second,
				KeepAlive: 60 * time.Second,
			}).DialContext,
			ForceAttemptHTTP2:     true,
			MaxIdleConns:          100,
			MaxIdleConnsPerHost:   100,
			IdleConnTimeout:       90 * time.Second,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}

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

	// 创建处理请求的函数
	handleRequest := func(w http.ResponseWriter, r *http.Request) {
		// 解析目标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
}