//go:generate go run .
package main

import (
	"archive/zip"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"runtime"

	"github.com/spf13/afero"
	"github.com/spf13/afero/zipfs"
	"github.com/uozi-tech/cosy/logger"
)

// GitHubRelease represents the structure of GitHub's release API response
type GitHubRelease struct {
	TagName string `json:"tag_name"`
}

const (
	githubAPIURL = "https://api.github.com/repos/go-acme/lego/releases/latest"
	configDir    = "internal/cert/config"
)

func main() {
	logger.Init("release")

	_, file, _, ok := runtime.Caller(0)
	if !ok {
		logger.Error("Unable to get the current file")
		return
	}
	basePath := filepath.Join(filepath.Dir(file), "../../")

	// Get the latest release tag
	tag, err := getLatestReleaseTag()
	if err != nil {
		logger.Errorf("Error getting latest release tag: %v\n", err)
		os.Exit(1)
	}
	logger.Infof("Latest release tag: %s", tag)

	zipFile, err := downloadAndExtract(tag)
	if err != nil {
		logger.Errorf("Error downloading and extracting: %v\n", err)
		os.Exit(1)
	}

	if err := copyTomlFiles(zipFile, basePath, tag); err != nil {
		logger.Errorf("Error copying TOML files: %v\n", err)
		os.Exit(1)
	}

	logger.Info("Successfully updated provider config")
}

// getLatestReleaseTag fetches the latest release tag from GitHub API
func getLatestReleaseTag() (string, error) {
	logger.Info("Fetching latest release tag...")

	req, err := http.NewRequest("GET", githubAPIURL, nil)
	if err != nil {
		return "", err
	}

	// Add User-Agent header to avoid GitHub API limitations
	req.Header.Set("User-Agent", "NGINX-UI-LegoConfigure")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("bad status from GitHub API: %s", resp.Status)
	}

	var release GitHubRelease
	if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
		return "", err
	}

	if release.TagName == "" {
		return "", fmt.Errorf("no tag name found in the latest release")
	}

	return release.TagName, nil
}

// downloadAndExtract downloads the lego repository for a specific tag and extracts it
func downloadAndExtract(tag string) (string, error) {
	downloadURL := fmt.Sprintf("https://github.com/go-acme/lego/archive/refs/tags/%s.zip", tag)

	// Download the file
	logger.Infof("Downloading lego repository for tag %s...", tag)
	resp, err := http.Get(downloadURL)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("bad status: %s", resp.Status)
	}

	// Create the file
	out, err := os.CreateTemp("", "lego-"+tag+".zip")
	if err != nil {
		return "", err
	}
	defer out.Close()

	// Write the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return "", err
	}

	return out.Name(), nil
}

func copyTomlFiles(zipFile, basePath, tag string) error {
	// Open the zip file
	logger.Info("Extracting files...")
	zipReader, err := zip.OpenReader(zipFile)
	if err != nil {
		return err
	}
	defer zipReader.Close()

	// Extract files
	tag = strings.TrimPrefix(tag, "v")
	zfs := zipfs.New(&zipReader.Reader)
	afero.Walk(zfs, "./lego-"+tag+"/providers", func(path string, info os.FileInfo, err error) error {
		if info == nil || info.IsDir() {
			return nil
		}
		if !strings.HasSuffix(info.Name(), ".toml") {
			return nil
		}
		if err != nil {
			return err
		}
		data, err := afero.ReadFile(zfs, path)
		if err != nil {
			return err
		}
		// Write to the destination file
		destPath := filepath.Join(basePath, configDir, info.Name())
		if err := os.WriteFile(destPath, data, 0644); err != nil {
			return err
		}
		logger.Infof("Copied: %s", info.Name())
		return nil
	})

	// Clean up zip file
	return os.Remove(zipFile)
}
