package main

import (
	"bufio"
	"embed"
	"flag"
	"fmt"
	"io/fs"
	"net"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// 使用 go:embed 强制将 docs 文件夹及其所有内容打包进二进制文件
//go:embed docs/*
var content embed.FS

func main() {
	// --- 1. 命令行参数配置 ---
	// 定义 -port 参数，默认为 -1（表示未指定，需要交互选择）
	portPtr := flag.Int("port", -1, "指定运行端口 (例如: 8080)，不指定则显示交互菜单")
	flag.Parse()

	// 如果没有通过命令行指定端口，显示交互式菜单
	port := *portPtr
	if port == -1 {
		port = showPortMenu()
	}

	// --- 2. 静态资源处理 ---
	// 获取 docs 子目录的文件系统句柄
	distFS, err := fs.Sub(content, "docs")
	if err != nil {
		fmt.Printf("错误: 无法访问嵌入的 docs 目录: %v\n", err)
		return
	}

	fileServer := http.FileServer(http.FS(distFS))

	// 自定义路由处理：支持静态文件和 SPA (单页应用) 回退
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// 移除路径前缀的斜杠
		path := strings.TrimPrefix(r.URL.Path, "/")

		// 尝试打开文件
		f, err := distFS.Open(path)
		if err != nil {
			// 如果文件不存在（或者是访问根目录），则回退到 index.html
			// 这是为了支持 Vue Router 的 History 模式
			indexData, err := fs.ReadFile(distFS, "index.html")
			if err != nil {
				http.Error(w, "Index.html not found in embedded docs", http.StatusNotFound)
				return
			}
			w.Header().Set("Content-Type", "text/html; charset=utf-8")
			http.ServeContent(w, r, "index.html", time.Now(), strings.NewReader(string(indexData)))
			return
		}
		f.Close()

		// 如果文件存在，使用标准文件服务器响应
		fileServer.ServeHTTP(w, r)
	})

	// --- 3. 端口监听逻辑 ---
	addr := fmt.Sprintf("0.0.0.0:%d", port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		fmt.Printf("错误: 端口 %d 已被占用或监听失败: %v\n", port, err)
		// 停留 5 秒让用户看到错误信息
		time.Sleep(5 * time.Second)
		os.Exit(1)
	}

	actualPort := listener.Addr().(*net.TCPAddr).Port
	localUrl := fmt.Sprintf("http://localhost:%d", actualPort)
	lanUrl := fmt.Sprintf("http://%s:%d", getLocalIp(), actualPort)

	// --- 4. 控制台信息展示 ---
	fmt.Println("=======================================")
	fmt.Printf("OGame Vue Ts 服务启动成功！\n")
	fmt.Printf("启动时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
	fmt.Printf("本地访问: %s\n", localUrl)
	fmt.Printf("局域网访问: %s\n", lanUrl)
	if port != 0 {
		fmt.Printf("运行模式: 固定端口 (%d)\n", port)
	} else {
		fmt.Printf("运行模式: 自动分配端口\n")
	}
	fmt.Println("=======================================")
	fmt.Println("提示: 请勿关闭此控制台窗口，否则服务将停止。")
	fmt.Println("--- 实时访问日志 ---")

	// --- 5. 自动打开浏览器并启动服务 ---
	go openBrowser(localUrl)

	err = http.Serve(listener, nil)
	if err != nil {
		fmt.Printf("服务运行异常: %v\n", err)
	}
}

// 获取本机局域网 IP
func getLocalIp() string {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "localhost"
	}
	for _, address := range addrs {
		if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				return ipnet.IP.String()
			}
		}
	}
	return "localhost"
}

// 根据不同操作系统自动打开默认浏览器
func openBrowser(url string) {
	var cmd string
	var args []string

	switch runtime.GOOS {
	case "windows":
		cmd = "cmd"
		args = []string{"/c", "start", url}
	case "darwin":
		cmd = "open"
		args = []string{url}
	default: // linux
		cmd = "xdg-open"
		args = []string{url}
	}

	_ = exec.Command(cmd, args...).Start()
}

// 显示端口选择菜单
func showPortMenu() int {
	reader := bufio.NewReader(os.Stdin)

	fmt.Println("=======================================")
	fmt.Println("       OGame Vue Ts 服务器启动")
	fmt.Println("=======================================")
	fmt.Println()
	fmt.Println("请选择端口模式:")
	fmt.Println("  [1] 随机端口 (自动分配可用端口)")
	fmt.Println("  [2] 自定义端口 (指定固定端口)")
	fmt.Println()
	fmt.Print("请输入选项 (1/2): ")

	for {
		input, _ := reader.ReadString('\n')
		input = strings.TrimSpace(input)

		switch input {
		case "1", "":
			fmt.Println("\n已选择: 随机端口模式")
			return 0
		case "2":
			return inputCustomPort(reader)
		default:
			fmt.Print("无效输入，请输入 1 或 2: ")
		}
	}
}

// 输入自定义端口
func inputCustomPort(reader *bufio.Reader) int {
	fmt.Print("请输入端口号 (1-65535，推荐: 8080): ")

	for {
		input, _ := reader.ReadString('\n')
		input = strings.TrimSpace(input)

		// 如果直接回车，使用默认端口 8080
		if input == "" {
			fmt.Println("\n已选择: 固定端口 8080")
			return 8080
		}

		port, err := strconv.Atoi(input)
		if err != nil || port < 1 || port > 65535 {
			fmt.Print("无效端口号，请输入 1-65535 之间的数字: ")
			continue
		}

		fmt.Printf("\n已选择: 固定端口 %d\n", port)
		return port
	}
}