package provision

import (
	"archive/tar"
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"

	"zlh-agent/internal/state"
)

// InstallJava downloads and extracts the Java runtime, then creates a stable
// symlink at /opt/zlh/runtime/java → <version>/bin/java
func InstallJava(cfg state.Config) error {
	if cfg.JavaPath == "" {
		return fmt.Errorf("java_path missing in config")
	}

	url := BuildArtifactURL(cfg.JavaPath)
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("download java from %s: %w", url, err)
	}
	defer resp.Body.Close()

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

	runtimeRoot := JavaDir(cfg)
	if err := os.MkdirAll(runtimeRoot, 0755); err != nil {
		return fmt.Errorf("create java root dir: %w", err)
	}

	// Extract into temp directory
	tmpDir := filepath.Join(runtimeRoot, "tmp-extract")
	_ = os.RemoveAll(tmpDir)
	if err := os.MkdirAll(tmpDir, 0755); err != nil {
		return fmt.Errorf("create tmp java dir: %w", err)
	}

	if err := extractTarGz(resp.Body, tmpDir); err != nil {
		return fmt.Errorf("extract java: %w", err)
	}

	// Detect root folder (JDK name)
	entries, err := os.ReadDir(tmpDir)
	if err != nil {
		return fmt.Errorf("read tmp java dir: %w", err)
	}
	if len(entries) != 1 || !entries[0].IsDir() {
		return fmt.Errorf("java extract: unexpected folder layout in %s", tmpDir)
	}

	rootFolder := entries[0].Name()
	versionPath := filepath.Join(runtimeRoot, rootFolder)

	// Move extracted Java dir into runtime/
	_ = os.RemoveAll(versionPath)
	if err := os.Rename(filepath.Join(tmpDir, rootFolder), versionPath); err != nil {
		return fmt.Errorf("move java folder: %w", err)
	}

	// Cleanup temp
	_ = os.RemoveAll(tmpDir)

	// Make java binary executable
	javaBin := filepath.Join(versionPath, "bin", "java")
	if err := os.Chmod(javaBin, 0755); err != nil {
		return fmt.Errorf("chmod java binary: %w", err)
	}

	// Symlink: java → <version>/bin/java
	linkPath := filepath.Join(JavaRoot, "java")
	_ = os.Remove(linkPath)
	if err := os.Symlink(javaBin, linkPath); err != nil {
		return fmt.Errorf("create java symlink: %w", err)
	}

	fmt.Println("[java] Installed runtime:", versionPath, "->", linkPath)
	return nil
}

// extractTarGz restores the original signature returning only "error"
func extractTarGz(r io.Reader, dest string) error {
	gz, err := gzip.NewReader(r)
	if err != nil {
		return err
	}
	defer gz.Close()

	tr := tar.NewReader(gz)

	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}

		target := filepath.Join(dest, hdr.Name)

		switch hdr.Typeflag {

		case tar.TypeDir:
			if err := os.MkdirAll(target, 0755); err != nil {
				return err
			}

		case tar.TypeReg:
			if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
				return err
			}

			f, err := os.Create(target)
			if err != nil {
				return err
			}
			if _, err := io.Copy(f, tr); err != nil {
				f.Close()
				return err
			}
			f.Close()
		}
	}

	return nil
}
