package world

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

// Summary describes a discovered Minecraft world directory.
type Summary struct {
	Name        string `json:"name"`
	Path        string `json:"path"`
	SizeBytes   int64  `json:"sizeBytes"`
	HasLevelDat bool   `json:"hasLevelDat"`
}

// ValidateWorld checks that a world directory contains level.dat.
func ValidateWorld(dataPath, worldName string) error {
	worldName = strings.TrimSpace(worldName)
	if worldName == "" || strings.Contains(worldName, "..") || strings.ContainsAny(worldName, `/\`) {
		return fmt.Errorf("invalid world name")
	}

	worldDir := filepath.Join(dataPath, worldName)
	levelDat := filepath.Join(worldDir, "level.dat")

	info, err := os.Stat(levelDat)
	if err != nil {
		return fmt.Errorf("level.dat not found in world directory")
	}
	if info.IsDir() {
		return fmt.Errorf("level.dat is not a file")
	}

	return nil
}

// ListWorlds scans the server data directory for world folders.
func ListWorlds(dataPath, activeWorldName string) ([]Summary, error) {
	entries, err := os.ReadDir(dataPath)
	if err != nil {
		return nil, err
	}

	worlds := make([]Summary, 0)

	for _, entry := range entries {
		if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") {
			continue
		}

		worldPath := filepath.Join(dataPath, entry.Name())
		levelDat := filepath.Join(worldPath, "level.dat")
		hasLevelDat := false
		if info, err := os.Stat(levelDat); err == nil && !info.IsDir() {
			hasLevelDat = true
		}

		size, err := dirSize(worldPath)
		if err != nil {
			return nil, err
		}

		worlds = append(worlds, Summary{
			Name:        entry.Name(),
			Path:        entry.Name(),
			SizeBytes:   size,
			HasLevelDat: hasLevelDat,
		})
	}

	if activeWorldName != "" {
		found := false
		for _, world := range worlds {
			if world.Name == activeWorldName {
				found = true
				break
			}
		}
		if !found {
			if err := ValidateWorld(dataPath, activeWorldName); err == nil {
				size, sizeErr := dirSize(filepath.Join(dataPath, activeWorldName))
				if sizeErr != nil {
					return nil, sizeErr
				}
				worlds = append(worlds, Summary{
					Name:        activeWorldName,
					Path:        activeWorldName,
					SizeBytes:   size,
					HasLevelDat: true,
				})
			}
		}
	}

	return worlds, nil
}

func dirSize(root string) (int64, error) {
	var total int64

	err := filepath.Walk(root, func(_ string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			total += info.Size()
		}
		return nil
	})

	return total, err
}
