package platform

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
)

var exportExecCommand = exec.Command

func formatMountTargetError(target RemovableTarget, raw string, err error) error {
	msg := strings.TrimSpace(raw)
	fstype := strings.ToLower(strings.TrimSpace(target.FSType))
	if fstype == "exfat" && strings.Contains(strings.ToLower(msg), "unknown filesystem type 'exfat'") {
		return fmt.Errorf("mount %s: exFAT support is missing in this ISO build: %w", target.Device, err)
	}
	if msg == "" {
		return err
	}
	return fmt.Errorf("%s: %w", msg, err)
}

func removableTargetReadOnly(fields map[string]string) bool {
	if fields["RO"] == "1" {
		return true
	}
	switch strings.ToLower(strings.TrimSpace(fields["FSTYPE"])) {
	case "iso9660", "squashfs":
		return true
	default:
		return false
	}
}

func ensureWritableMountpoint(mountpoint string) error {
	probe, err := os.CreateTemp(mountpoint, ".bee-write-test-*")
	if err != nil {
		return fmt.Errorf("target filesystem is not writable: %w", err)
	}
	name := probe.Name()
	if closeErr := probe.Close(); closeErr != nil {
		_ = os.Remove(name)
		return closeErr
	}
	if err := os.Remove(name); err != nil {
		return err
	}
	return nil
}

func (s *System) ListRemovableTargets() ([]RemovableTarget, error) {
	raw, err := exportExecCommand("lsblk", "-P", "-o", "NAME,TYPE,PKNAME,RM,RO,FSTYPE,MOUNTPOINT,SIZE,LABEL,MODEL").Output()
	if err != nil {
		return nil, err
	}

	var out []RemovableTarget
	for _, line := range strings.Split(strings.TrimSpace(string(raw)), "\n") {
		if strings.TrimSpace(line) == "" {
			continue
		}
		fields := parseLSBLKPairs(line)
		deviceType := fields["TYPE"]
		if deviceType == "rom" || deviceType == "loop" {
			continue
		}

		removable := fields["RM"] == "1"
		if !removable {
			if parent := fields["PKNAME"]; parent != "" {
				if data, err := os.ReadFile(filepath.Join("/sys/class/block", parent, "removable")); err == nil {
					removable = strings.TrimSpace(string(data)) == "1"
				}
			}
		}
		if !removable || fields["FSTYPE"] == "" || removableTargetReadOnly(fields) {
			continue
		}

		out = append(out, RemovableTarget{
			Device:     "/dev/" + fields["NAME"],
			FSType:     fields["FSTYPE"],
			Size:       fields["SIZE"],
			Label:      fields["LABEL"],
			Model:      fields["MODEL"],
			Mountpoint: fields["MOUNTPOINT"],
		})
	}

	sort.Slice(out, func(i, j int) bool { return out[i].Device < out[j].Device })
	return out, nil
}

func (s *System) ExportFileToTarget(src string, target RemovableTarget) (dst string, retErr error) {
	if src == "" || target.Device == "" {
		return "", fmt.Errorf("source and target are required")
	}
	if _, err := os.Stat(src); err != nil {
		return "", err
	}

	mountpoint := strings.TrimSpace(target.Mountpoint)
	mountedHere := false
	mounted := mountpoint != ""
	if mountpoint == "" {
		mountpoint = filepath.Join("/tmp", "bee-export-"+filepath.Base(target.Device))
		if err := os.MkdirAll(mountpoint, 0755); err != nil {
			return "", err
		}
		if raw, err := exportExecCommand("mount", target.Device, mountpoint).CombinedOutput(); err != nil {
			_ = os.Remove(mountpoint)
			return "", formatMountTargetError(target, string(raw), err)
		}
		mountedHere = true
		mounted = true
	}
	defer func() {
		if !mounted {
			return
		}
		_ = exportExecCommand("sync").Run()
		if raw, err := exportExecCommand("umount", mountpoint).CombinedOutput(); err != nil && retErr == nil {
			msg := strings.TrimSpace(string(raw))
			if msg == "" {
				retErr = err
			} else {
				retErr = fmt.Errorf("%s: %w", msg, err)
			}
		}
		if mountedHere {
			_ = os.Remove(mountpoint)
		}
	}()

	if err := ensureWritableMountpoint(mountpoint); err != nil {
		return "", err
	}

	filename := filepath.Base(src)
	dst = filepath.Join(mountpoint, filename)
	data, err := os.ReadFile(src)
	if err != nil {
		return "", err
	}
	if err := os.WriteFile(dst, data, 0644); err != nil {
		return "", err
	}

	return dst, nil
}
