package minio

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

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

type Adapter struct {
    Runner osexec.Runner
    EnvPath string
}

func NewAdapter(runner osexec.Runner, envPath string) *Adapter {
    if envPath == "" {
        envPath = "/etc/minio/minio.env"
    }
    return &Adapter{Runner: runner, EnvPath: envPath}
}

type Settings struct {
    AccessKey string `json:"access_key"`
    SecretKey string `json:"secret_key"`
    DataPath  string `json:"data_path"`
    Port      int    `json:"port"`
    TLS       bool   `json:"tls"`
}

// WriteEnv writes environment file used by MinIO service
func (a *Adapter) WriteEnv(ctx context.Context, s Settings) error {
    dir := filepath.Dir(a.EnvPath)
    if err := os.MkdirAll(dir, 0755); err != nil {
        return err
    }
    // env lines
    lines := []string{
        fmt.Sprintf("MINIO_ROOT_USER=%s", s.AccessKey),
        fmt.Sprintf("MINIO_ROOT_PASSWORD=%s", s.SecretKey),
        fmt.Sprintf("MINIO_VOLUMES=%s", s.DataPath),
    }
    if s.Port != 0 {
        lines = append(lines, fmt.Sprintf("MINIO_OPTS=--address :%d", s.Port))
    }
    content := strings.Join(lines, "\n") + "\n"
    tmp := filepath.Join(dir, ".minio.env.tmp")
    if err := os.WriteFile(tmp, []byte(content), 0600); err != nil {
        return err
    }
    if err := os.Rename(tmp, a.EnvPath); err != nil {
        return err
    }
    return nil
}

// Reload reloads minio service to pick up new env; prefer systemctl reload
func (a *Adapter) Reload(ctx context.Context) error {
    _, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "systemctl", "reload", "minio")
    if err == nil {
        return nil
    }
    // fallback to restart
    _, stderr, _, err = osexec.ExecWithRunner(a.Runner, ctx, "systemctl", "restart", "minio")
    if err != nil {
        return fmt.Errorf("minio reload/restart failed: %s", stderr)
    }
    return nil
}

// ConfigureMC configures mc alias to point to the MinIO service using given settings
func (a *Adapter) ConfigureMC(ctx context.Context, alias string, settings Settings) error {
    secure := "--insecure"
    if settings.TLS {
        secure = ""
    }
    // mc alias set <alias> <endpoint> <access> <secret> [--api S3v4]
    endpoint := fmt.Sprintf("http://127.0.0.1:%d", settings.Port)
    if settings.TLS {
        endpoint = fmt.Sprintf("https://127.0.0.1:%d", settings.Port)
    }
    _, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "mc", "alias", "set", alias, endpoint, settings.AccessKey, settings.SecretKey, secure)
    if err != nil {
        return fmt.Errorf("mc alias set failed: %s", stderr)
    }
    return nil
}

// ListBuckets uses mc to list buckets via alias
func (a *Adapter) ListBuckets(ctx context.Context, alias string) ([]string, error) {
    out, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "mc", "ls", "--json", alias)
    if err != nil {
        return nil, fmt.Errorf("mc ls failed: %s", stderr)
    }
    // parse JSON lines, each contains a 'key' or 'name' - in mc, `ls --json` returns 'key'
    var buckets []string
    lines := strings.Split(strings.TrimSpace(out), "\n")
    for _, l := range lines {
        var obj map[string]any
        if err := json.Unmarshal([]byte(l), &obj); err != nil {
            continue
        }
        if otype, ok := obj["type"].(string); ok && otype == "bucket" {
            if name, ok := obj["key"].(string); ok {
                buckets = append(buckets, name)
            }
        }
    }
    return buckets, nil
}

// CreateBucket uses mc to create a new bucket alias/<name>
func (a *Adapter) CreateBucket(ctx context.Context, alias, name string) error {
    _, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "mc", "mb", alias+"/"+name)
    if err != nil {
        return fmt.Errorf("mc mb failed: %s", stderr)
    }
    return nil
}
