package main

import (
	"fmt"
	"io"
	"io/fs"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// installPrefix is the base install directory.
// Binaries go to installPrefix/bin, assets and config go to installPrefix/.
const installPrefix = "/opt/weatherstation"

func main() {
	// Ensure target directories exist
	binDir := filepath.Join(installPrefix, "bin")
	if err := os.MkdirAll(binDir, 0o755); err != nil {
		fatalf("创建目录失败: %s: %v", binDir, err)
	}

	// Build all service-* under cmd/
	serviceDirs, err := findServiceDirs()
	if err != nil {
		fatalf("扫描服务目录失败: %v", err)
	}
	if len(serviceDirs) == 0 {
		fatalf("未发现任何 service-* 微服务目录")
	}

	for _, svc := range serviceDirs {
		out := filepath.Join(binDir, svc)
		// 必须使用相对前缀 ./，否则 go 会将其当成标准库/模块路径
		pkg := "./" + filepath.ToSlash(filepath.Join("cmd", svc))
		fmt.Printf("编译 %s -> %s\n", pkg, out)
		if err := run("go", "build", "-o", out, pkg); err != nil {
			fatalf("编译失败 %s: %v", pkg, err)
		}
	}

	// Copy templates, static, config.yaml to installPrefix
	// Replace existing files/directories
	if err := copyDirReplacing("templates", filepath.Join(installPrefix, "templates")); err != nil {
		fatalf("复制 templates 失败: %v", err)
	}
	if err := copyDirReplacing("static", filepath.Join(installPrefix, "static")); err != nil {
		fatalf("复制 static 失败: %v", err)
	}
	if err := copyFileReplacing("config.yaml", filepath.Join(installPrefix, "config.yaml"), 0o644); err != nil {
		// 配置文件可能不存在于仓库，但按照需求尝试复制，若不存在给出提示
		if !os.IsNotExist(err) {
			fatalf("复制 config.yaml 失败: %v", err)
		} else {
			fmt.Println("提示: 仓库根目录未找到 config.yaml，跳过复制")
		}
	}

	fmt.Printf("完成: 微服务安装于 %s，资源已同步到 %s\n", binDir, installPrefix)
}

func fatalf(format string, a ...any) {
	fmt.Fprintf(os.Stderr, format+"\n", a...)
	os.Exit(1)
}

// findServiceDirs returns names like service-api, service-udp under cmd/.
func findServiceDirs() ([]string, error) {
	entries, err := os.ReadDir("cmd")
	if err != nil {
		return nil, err
	}
	var list []string
	for _, e := range entries {
		if !e.IsDir() {
			continue
		}
		name := e.Name()
		if strings.HasPrefix(name, "service-") {
			// ensure main.go exists to be buildable
			if _, err := os.Stat(filepath.Join("cmd", name, "main.go")); err == nil {
				list = append(list, name)
			}
		}
	}
	return list, nil
}

func run(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func copyDirReplacing(src, dst string) error {
	st, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !st.IsDir() {
		return fmt.Errorf("%s 不是目录", src)
	}
	// Remove destination to ensure clean replace
	if err := os.RemoveAll(dst); err != nil {
		return err
	}
	if err := os.MkdirAll(dst, 0o755); err != nil {
		return err
	}
	return filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		rel, err := filepath.Rel(src, path)
		if err != nil {
			return err
		}
		target := filepath.Join(dst, rel)
		if d.IsDir() {
			if rel == "." {
				return nil
			}
			return os.MkdirAll(target, 0o755)
		}
		return copyFileReplacing(path, target, 0o644)
	})
}

func copyFileReplacing(src, dst string, perm os.FileMode) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
		return err
	}
	out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, perm)
	if err != nil {
		return err
	}
	defer func() { _ = out.Close() }()

	if _, err := io.Copy(out, in); err != nil {
		return err
	}
	return out.Sync()
}
