package platform

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

func TestExportFileToTargetUnmountsExistingMountpoint(t *testing.T) {
	tmp := t.TempDir()
	src := filepath.Join(tmp, "bundle.tar.gz")
	mountpoint := filepath.Join(tmp, "mnt")
	if err := os.MkdirAll(mountpoint, 0755); err != nil {
		t.Fatalf("mkdir mountpoint: %v", err)
	}
	if err := os.WriteFile(src, []byte("bundle"), 0644); err != nil {
		t.Fatalf("write src: %v", err)
	}

	var calls [][]string
	oldExec := exportExecCommand
	exportExecCommand = func(name string, args ...string) *exec.Cmd {
		calls = append(calls, append([]string{name}, args...))
		return exec.Command("sh", "-c", "exit 0")
	}
	t.Cleanup(func() { exportExecCommand = oldExec })

	s := &System{}
	dst, err := s.ExportFileToTarget(src, RemovableTarget{
		Device:     "/dev/sdb1",
		Mountpoint: mountpoint,
	})
	if err != nil {
		t.Fatalf("ExportFileToTarget error: %v", err)
	}
	if got, want := dst, filepath.Join(mountpoint, "bundle.tar.gz"); got != want {
		t.Fatalf("dst=%q want %q", got, want)
	}
	if _, err := os.Stat(filepath.Join(mountpoint, "bundle.tar.gz")); err != nil {
		t.Fatalf("exported file missing: %v", err)
	}

	foundUmount := false
	for _, call := range calls {
		if len(call) == 2 && call[0] == "umount" && call[1] == mountpoint {
			foundUmount = true
			break
		}
	}
	if !foundUmount {
		t.Fatalf("expected umount %q call, got %#v", mountpoint, calls)
	}
}

func TestExportFileToTargetRejectsNonWritableMountpoint(t *testing.T) {
	tmp := t.TempDir()
	src := filepath.Join(tmp, "bundle.tar.gz")
	mountpoint := filepath.Join(tmp, "mnt")
	if err := os.MkdirAll(mountpoint, 0755); err != nil {
		t.Fatalf("mkdir mountpoint: %v", err)
	}
	if err := os.WriteFile(src, []byte("bundle"), 0644); err != nil {
		t.Fatalf("write src: %v", err)
	}
	if err := os.Chmod(mountpoint, 0555); err != nil {
		t.Fatalf("chmod mountpoint: %v", err)
	}

	oldExec := exportExecCommand
	exportExecCommand = func(name string, args ...string) *exec.Cmd {
		return exec.Command("sh", "-c", "exit 0")
	}
	t.Cleanup(func() { exportExecCommand = oldExec })

	s := &System{}
	_, err := s.ExportFileToTarget(src, RemovableTarget{
		Device:     "/dev/sdb1",
		Mountpoint: mountpoint,
	})
	if err == nil {
		t.Fatal("expected error for non-writable mountpoint")
	}
	if !strings.Contains(err.Error(), "target filesystem is not writable") {
		t.Fatalf("err=%q want writable message", err)
	}
}

func TestListRemovableTargetsSkipsReadOnlyMedia(t *testing.T) {
	oldExec := exportExecCommand
	lsblkOut := `NAME="sda1" TYPE="part" PKNAME="sda" RM="1" RO="1" FSTYPE="iso9660" MOUNTPOINT="/run/live/medium" SIZE="3.7G" LABEL="BEE" MODEL=""
NAME="sdb1" TYPE="part" PKNAME="sdb" RM="1" RO="0" FSTYPE="vfat" MOUNTPOINT="/media/bee/USB" SIZE="29.8G" LABEL="USB" MODEL=""`
	exportExecCommand = func(name string, args ...string) *exec.Cmd {
		cmd := exec.Command("sh", "-c", "printf '%s\n' \"$LSBLK_OUT\"")
		cmd.Env = append(os.Environ(), "LSBLK_OUT="+lsblkOut)
		return cmd
	}
	t.Cleanup(func() { exportExecCommand = oldExec })

	s := &System{}
	targets, err := s.ListRemovableTargets()
	if err != nil {
		t.Fatalf("ListRemovableTargets error: %v", err)
	}
	if len(targets) != 1 {
		t.Fatalf("len(targets)=%d want 1 (%+v)", len(targets), targets)
	}
	if got := targets[0].Device; got != "/dev/sdb1" {
		t.Fatalf("device=%q want /dev/sdb1", got)
	}
}
