package guestconfig

import (
	"sort"
	"strconv"
	"strings"
)

type MountSpec struct {
	Source  string
	Target  string
	FSType  string
	Options []string
	Dump    int
	Pass    int
}

func (m MountSpec) String() string {
	options := strings.Join(compactStrings(m.Options), ",")
	if options == "" {
		options = "defaults"
	}
	return strings.Join([]string{
		m.Source,
		m.Target,
		m.FSType,
		options,
		strconv.Itoa(m.Dump),
		strconv.Itoa(m.Pass),
	}, " ")
}

type Builder struct {
	files       map[string][]byte
	dropTargets map[string]struct{}
	mounts      map[string]MountSpec
	order       []string
}

func NewBuilder() *Builder {
	return &Builder{
		files:       make(map[string][]byte),
		dropTargets: make(map[string]struct{}),
		mounts:      make(map[string]MountSpec),
	}
}

func (b *Builder) WriteFile(path string, data []byte) {
	if b.files == nil {
		b.files = make(map[string][]byte)
	}
	b.files[path] = append([]byte(nil), data...)
}

func (b *Builder) DropMountTarget(target string) {
	target = strings.TrimSpace(target)
	if target == "" {
		return
	}
	if b.dropTargets == nil {
		b.dropTargets = make(map[string]struct{})
	}
	b.dropTargets[target] = struct{}{}
}

func (b *Builder) AddMount(spec MountSpec) {
	spec.Source = strings.TrimSpace(spec.Source)
	spec.Target = strings.TrimSpace(spec.Target)
	spec.FSType = strings.TrimSpace(spec.FSType)
	spec.Options = compactStrings(spec.Options)
	if spec.Source == "" || spec.Target == "" || spec.FSType == "" {
		return
	}
	if b.mounts == nil {
		b.mounts = make(map[string]MountSpec)
	}
	if _, exists := b.mounts[spec.Target]; !exists {
		b.order = append(b.order, spec.Target)
	}
	b.mounts[spec.Target] = spec
}

func (b *Builder) Files() map[string][]byte {
	if len(b.files) == 0 {
		return nil
	}
	keys := b.FilePaths()
	out := make(map[string][]byte, len(keys))
	for _, path := range keys {
		out[path] = append([]byte(nil), b.files[path]...)
	}
	return out
}

func (b *Builder) FilePaths() []string {
	keys := make([]string, 0, len(b.files))
	for path := range b.files {
		keys = append(keys, path)
	}
	sort.Strings(keys)
	return keys
}

func (b *Builder) RenderFSTab(existing string) string {
	lines := strings.Split(existing, "\n")
	out := make([]string, 0, len(lines)+len(b.mounts))
	managedTargets := make(map[string]struct{}, len(b.mounts))
	for target := range b.mounts {
		managedTargets[target] = struct{}{}
	}
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if trimmed == "" {
			continue
		}
		target := mountTarget(trimmed)
		if target != "" {
			if _, drop := b.dropTargets[target]; drop {
				continue
			}
			if _, managed := managedTargets[target]; managed {
				continue
			}
		}
		out = append(out, line)
	}
	for _, target := range b.order {
		out = append(out, b.mounts[target].String())
	}
	return strings.Join(out, "\n") + "\n"
}

func mountTarget(line string) string {
	fields := strings.Fields(line)
	if len(fields) < 2 {
		return ""
	}
	return fields[1]
}

func compactStrings(values []string) []string {
	if len(values) == 0 {
		return nil
	}
	out := make([]string, 0, len(values))
	seen := make(map[string]struct{}, len(values))
	for _, value := range values {
		value = strings.TrimSpace(value)
		if value == "" {
			continue
		}
		if _, ok := seen[value]; ok {
			continue
		}
		seen[value] = struct{}{}
		out = append(out, value)
	}
	return out
}
