package dispatch

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"time"
)

// instanceMeta is the on-disk sidecar written per instance after successful
// Create. It lets the agent rehydrate its in-memory records after a restart,
// so operator commands (stop, etc.) continue to work against instances that
// outlived the agent process.
//
// Stored at <metaDir>/<instance_id>.json. Mode 0600 because it carries the
// RCON password. Deleted on stop.
type instanceMeta struct {
	InstanceID     string            `json:"instance_id"`
	ContainerID    string            `json:"container_id"`
	ModuleID       string            `json:"module_id"`
	// Branch is the normalized Steam branch the install came from (warm-seed
	// matching key). Empty on sidecars written before this feature.
	Branch         string            `json:"branch,omitempty"`
	DataPath       string            `json:"data_path"`
	BrowseableRoot string            `json:"browseable_root,omitempty"`
	RCONAddr       string            `json:"rcon_addr,omitempty"`
	RCONPassword   string            `json:"rcon_password,omitempty"`
	ConfigValues   map[string]string `json:"config_values,omitempty"`
	CreatedAt      time.Time         `json:"created_at"`
}

func (d *Dispatcher) metaPath(instanceID string) string {
	return filepath.Join(d.metaDir, instanceID+".json")
}

func (d *Dispatcher) writeMeta(rec *instanceRecord) error {
	if d.metaDir == "" {
		return nil
	}
	if err := os.MkdirAll(d.metaDir, 0o755); err != nil {
		return fmt.Errorf("mkdir meta dir: %w", err)
	}
	m := instanceMeta{
		InstanceID:     rec.InstanceID,
		ContainerID:    rec.ContainerID,
		ModuleID:       rec.ModuleID,
		Branch:         rec.Branch,
		DataPath:       rec.DataPath,
		BrowseableRoot: rec.BrowseableRoot,
		RCONAddr:       rec.RCONAddr,
		RCONPassword:   rec.RCONPassword,
		CreatedAt:      time.Now(),
	}
	data, err := json.MarshalIndent(m, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal meta: %w", err)
	}
	if err := os.WriteFile(d.metaPath(rec.InstanceID), data, 0o600); err != nil {
		return fmt.Errorf("write meta: %w", err)
	}
	return nil
}

func (d *Dispatcher) deleteMeta(instanceID string) {
	if d.metaDir == "" {
		return
	}
	if err := os.Remove(d.metaPath(instanceID)); err != nil && !errors.Is(err, fs.ErrNotExist) {
		d.log.Warn("delete meta failed", "instance_id", instanceID, "err", err)
	}
}

func (d *Dispatcher) loadAllMeta() ([]instanceMeta, error) {
	if d.metaDir == "" {
		return nil, nil
	}
	entries, err := os.ReadDir(d.metaDir)
	if err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			return nil, nil
		}
		return nil, err
	}
	var out []instanceMeta
	for _, e := range entries {
		if e.IsDir() {
			continue
		}
		name := e.Name()
		if filepath.Ext(name) != ".json" {
			continue
		}
		data, err := os.ReadFile(filepath.Join(d.metaDir, name))
		if err != nil {
			d.log.Warn("read meta failed", "file", name, "err", err)
			continue
		}
		var m instanceMeta
		if err := json.Unmarshal(data, &m); err != nil {
			d.log.Warn("bad meta file", "file", name, "err", err)
			continue
		}
		if m.InstanceID == "" {
			continue
		}
		out = append(out, m)
	}
	return out, nil
}
