package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"gopkg.in/yaml.v2"
)

type Tag struct {
	Tag       string   `yaml:"tag"`
	Platforms []string `yaml:"platforms"`
}

type Image struct {
	Name string `yaml:"name"`
	From string `yaml:"from,omitempty"` // `from` 为可选字段
	Tags []Tag  `yaml:"tags"`
}

type Config struct {
	Target string  `yaml:"target"`
	Images []Image `yaml:"images"`
}

func main() {
	// 记录开始时间
	startTime := time.Now()

	// 创建日志文件名
	logFileName := fmt.Sprintf("build_%s.log", startTime.Format("20060102"))
	logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		logErrorInfo(logFile, time.Now(),
			fmt.Sprintf("Error opening log file: %v", err))
		return
	}
	defer logFile.Close()

	// 记录程序启动时间
	logStartTime(logFile, startTime)

	// 读取 YAML 文件
	data, err := ioutil.ReadFile("images.yaml")
	if err != nil {
		logErrorInfo(logFile, time.Now(),
			fmt.Sprintf("Error reading YAML file: %v", err))
		return
	}

	// 解析 YAML 文件
	var config Config
	if err := yaml.Unmarshal(data, &config); err != nil {
		logErrorInfo(logFile, time.Now(),
			fmt.Sprintf("Error parsing YAML: %v", err))
		return
	}

	totalImages := 0
	totalTags := 0

	// 遍历每个镜像信息
	for _, image := range config.Images {
		// 如果 `from` 字段为空，则将其设置为 `name`（Docker Hub 官方镜像）
		if image.From == "" {
			image.From = image.Name
		}

		imageBuilt := false // 记录是否构建过该镜像
		for _, tag := range image.Tags {
			totalTags++
			// 根据镜像生成 Dockerfile
			dockerfilePath := filepath.Join(".", fmt.Sprintf("Dockerfile.%s:%s", image.Name, tag.Tag))
			if err := generateDockerfile(image.From, tag.Tag, dockerfilePath); err != nil {
				logErrorInfo(logFile, time.Now(),
					fmt.Sprintf("Error generating Dockerfile for %s:%s: %v", image.Name, tag.Tag, err))
				continue
			}

			// 合并所有平台为一个字符串
			platforms := strings.Join(tag.Platforms, ",")
			// 构建 Docker 镜像命令，添加 target
			buildCmd := exec.Command("docker", "buildx", "build", "--push", "--platform", platforms, "-t", fmt.Sprintf("%s/%s:%s", config.Target, image.Name, tag.Tag), "-f", dockerfilePath, ".")
			buildCmd.Stdout = os.Stdout
			buildCmd.Stderr = os.Stderr

			// 记录开始构建时间
			buildStartTime := time.Now()
			if err := buildCmd.Run(); err != nil {
				logErrorInfo(logFile, time.Now(),
					fmt.Sprintf("Error building image %s:%s for platforms %s: %v", image.Name, tag.Tag, platforms, err))
				continue
			}
			// 记录构建结束时间
			buildEndTime := time.Now()

			// 记录构建日志
			logBuildInfo(logFile, buildStartTime, image.Name, tag.Tag, platforms, config.Target, buildEndTime.Sub(buildStartTime))
			imageBuilt = true // 标记该镜像已成功构建

			// 删除生成的 Dockerfile
			if err := os.Remove(dockerfilePath); err != nil {
				logErrorInfo(logFile, time.Now(),
					fmt.Sprintf("Error deleting Dockerfile %s: %v", dockerfilePath, err))
			}
		}
		if imageBuilt {
			totalImages++ // 仅在成功构建过一次时增加镜像计数
		}
	}

	// 计算总时间
	elapsedTime := time.Since(startTime)
	logCompletion(logFile, elapsedTime, totalImages, totalTags)
}

// 记录程序启动时间
func logStartTime(logFile *os.File, startTime time.Time) {
	logFile.WriteString(fmt.Sprintf("Process started at: %s\n", startTime.Format(time.RFC3339)))
}

// 记录构建信息
func logBuildInfo(logFile *os.File, startTime time.Time, imageName, tag string, platforms string, target string, duration time.Duration) {
	logFile.WriteString(fmt.Sprintf("%s | Image: %s | Tag: %s | Platforms: %s | Pushed to: %s/%s:%s | Duration: %v\n",
		startTime.Format(time.RFC3339), imageName, tag, platforms, target, imageName, tag, duration))
}

// 记录报错
func logErrorInfo(logFile *os.File, startTime time.Time, errorMessage string) {
	logFile.WriteString(fmt.Sprintf("%s | %s\n",
		startTime.Format(time.RFC3339), errorMessage))
}

// 记录完成时间和统计信息
func logCompletion(logFile *os.File, elapsedTime time.Duration, totalImages, totalTags int) {
	logFile.WriteString(fmt.Sprintf("Build completed at: %s\n", time.Now().Format(time.RFC3339)))
	logFile.WriteString(fmt.Sprintf("Total images built: %d\n", totalImages))
	logFile.WriteString(fmt.Sprintf("Total tags processed: %d\n", totalTags))
	logFile.WriteString(fmt.Sprintf("Total execution time: %v\n", elapsedTime))
}

// 生成 Dockerfile
func generateDockerfile(fromImage, tag, filePath string) error {
	dockerfileContent := fmt.Sprintf("FROM %s:%s\n", fromImage, tag)
	return ioutil.WriteFile(filePath, []byte(dockerfileContent), 0644)
}
