package incus

import (
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"
)

type IncusDriver struct{}

func NewIncusDriver() *IncusDriver {
	return &IncusDriver{}
}

func (d *IncusDriver) GetInstances(target string) ([]Instance, error) {
	// Command: incus list -f json

	var cmd *exec.Cmd
	if target == "" {
		cmd = exec.Command("incus", "list", "--format", "json")
	} else {
		cmd = exec.Command("incus", "list", target+":", "--format", "json")
	}

	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("failed to execute incus list: %w", err)
	}

	var instances []Instance
	err = json.Unmarshal(output, &instances)
	if err != nil {
		return nil, err
	}

	return instances, nil
}

func (d *IncusDriver) Sync(sourceInstance string, targetInstance string, targetHost string, targetPool string) error {
	// incus copy edge0 racker1:edge0-cold -s pool0 --mode push -p default -p net_edge0 --stateless --refresh
	// incus copy edge0 racker1:edge0-cold -s pool0 --mode push -p default -p net_edge0 --stateless

	instances, err := d.GetInstances(targetHost)
	if err != nil {
		return err
	}

	var cmd *exec.Cmd
	if len(instances) == 0 {
		cmd = exec.Command("incus", "copy", sourceInstance, targetHost+":"+targetInstance, "-s", targetPool, "--mode", "push", "--stateless")
	} else {
		cmd = exec.Command("incus", "copy", sourceInstance, targetHost+":"+targetInstance, "-s", targetPool, "--mode", "push", "--stateless", "--refresh")
	}
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to execute incus copy: %w (%s)", err, string(out))
	}

	return nil
}

func (d *IncusDriver) Backup(instance string, tags []string) error {
	// incus export ups - -q --compression=zstd --instance-only --optimized-storage | restic backup --stdin --stdin-filename ups.btrfs.zstd --tag instance

	// Create the incus export command
	incusCmd := exec.Command("incus", "export", instance, "-", "-q", "--compression=zstd", "--instance-only", "--optimized-storage")

	// Create the restic backup command
	resticCmd := exec.Command("restic", "backup", "--host", instance, "--stdin", "--stdin-filename", fmt.Sprintf("%s.btrfs.zstd", instance), "--tag", strings.Join(tags, ","))

	// Connect the output of incusCmd to the input of resticCmd
	pipe, err := incusCmd.StdoutPipe()
	if err != nil {
		return fmt.Errorf("failed to create pipe: %w", err)
	}
	resticCmd.Stdin = pipe

	// Start the incus export command
	if err := incusCmd.Start(); err != nil {
		return fmt.Errorf("failed to start incus export: %w", err)
	}

	// Start the restic backup command
	if err := resticCmd.Start(); err != nil {
		return fmt.Errorf("failed to start restic backup: %w", err)
	}

	// Wait for the incus export command to finish
	if err := incusCmd.Wait(); err != nil {
		return fmt.Errorf("incus export command failed: %w", err)
	}

	// Wait for the restic backup command to finish
	if err := resticCmd.Wait(); err != nil {
		return fmt.Errorf("restic backup command failed: %w", err)
	}

	return nil
}
