package nfs

import (
    "context"
    "fmt"
    "os"
    "path/filepath"
    "strings"

    "github.com/example/storage-appliance/internal/domain"
    "github.com/example/storage-appliance/internal/infra/osexec"
)

type Adapter struct {
    Runner      osexec.Runner
    ExportsPath string
}

func NewAdapter(runner osexec.Runner, exportsPath string) *Adapter {
    if exportsPath == "" {
        exportsPath = "/etc/exports"
    }
    return &Adapter{Runner: runner, ExportsPath: exportsPath}
}

// RenderExports renders the given shares into /etc/exports atomically
func (a *Adapter) RenderExports(ctx context.Context, shares []domain.Share) error {
    var lines []string
    for _, s := range shares {
        // default options for NFS export
        opts := "rw,sync,no_root_squash"
        if s.Type == "nfs" {
            // if options stored as JSON use it
            if sPath := s.Path; sPath != "" {
                // options may be in s.Name? No, for now use default
            }
        }
        lines = append(lines, fmt.Sprintf("%s %s", s.Path, opts))
    }
    content := strings.Join(lines, "\n") + "\n"

    dir := filepath.Dir(a.ExportsPath)
    tmp := filepath.Join(dir, ".exports.tmp")
    if err := os.WriteFile(tmp, []byte(content), 0644); err != nil {
        return err
    }
    // atomic rename
    if err := os.Rename(tmp, a.ExportsPath); err != nil {
        return err
    }
    return nil
}

// Apply runs exportfs -ra to apply exports
func (a *Adapter) Apply(ctx context.Context) error {
    _, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "exportfs", "-ra")
    if err != nil {
        return fmt.Errorf("exportfs failed: %s", stderr)
    }
    return nil
}

// Status checks systemd for nfs server status
func (a *Adapter) Status(ctx context.Context) (string, error) {
    // try common unit names
    names := []string{"nfs-server", "nfs-kernel-server"}
    for _, n := range names {
        out, _, _, err := osexec.ExecWithRunner(a.Runner, ctx, "systemctl", "is-active", n)
        if err == nil && strings.TrimSpace(out) != "" {
            return strings.TrimSpace(out), nil
        }
    }
    return "unknown", nil
}
