package regionmedic

import (
	"os"
	"path/filepath"
	"sort"
)

// ChunkCoord is a global chunk coordinate.
type ChunkCoord struct {
	X int `json:"x"`
	Z int `json:"z"`
}

// AffectedRegion is one region with corruption evidence (error_backup salvage
// files whose chunks fall inside it), plus whether its live region file exists.
type AffectedRegion struct {
	Region       RegionCoord  `json:"region"`
	ErrorBackups int          `json:"error_backups"`
	Chunks       []ChunkCoord `json:"chunks,omitempty"`
	RegionFile   string       `json:"region_file,omitempty"` // absolute path if present
	FilePresent  bool         `json:"file_present"`
}

// RegionDirScan is the result of scanning a 7DTD save Region/ directory.
type RegionDirScan struct {
	Dir             string           `json:"dir"`
	ErrorBackups    int              `json:"error_backups"`     // total salvage files
	ErrorBackupDirs int              `json:"error_backup_chunks"` // distinct chunks
	Affected        []AffectedRegion `json:"affected"`          // sorted by count desc
}

// ScanRegionDir lists error_backup_* salvage files in dir, maps each to its
// region, and clusters them. The returned Affected slice is sorted by
// ErrorBackups descending (most-damaged region first). A region appears only if
// it has at least one error_backup chunk.
func ScanRegionDir(dir string) (RegionDirScan, error) {
	out := RegionDirScan{Dir: dir}
	ents, err := os.ReadDir(dir)
	if err != nil {
		return out, err
	}

	// region -> set of distinct chunk coords (dedup .comp/.uncomp pairs)
	type acc struct {
		files  int
		chunks map[ChunkCoord]struct{}
	}
	clusters := map[RegionCoord]*acc{}

	for _, e := range ents {
		if e.IsDir() {
			continue
		}
		cx, cz, ok := ParseErrorBackup(e.Name())
		if !ok {
			continue
		}
		out.ErrorBackups++
		rc := RegionForChunk(cx, cz)
		a := clusters[rc]
		if a == nil {
			a = &acc{chunks: map[ChunkCoord]struct{}{}}
			clusters[rc] = a
		}
		a.files++
		a.chunks[ChunkCoord{X: cx, Z: cz}] = struct{}{}
	}

	for rc, a := range clusters {
		ar := AffectedRegion{Region: rc, ErrorBackups: a.files}
		for c := range a.chunks {
			ar.Chunks = append(ar.Chunks, c)
		}
		sort.Slice(ar.Chunks, func(i, j int) bool {
			if ar.Chunks[i].X != ar.Chunks[j].X {
				return ar.Chunks[i].X < ar.Chunks[j].X
			}
			return ar.Chunks[i].Z < ar.Chunks[j].Z
		})
		out.ErrorBackupDirs += len(ar.Chunks)
		rf := filepath.Join(dir, rc.FileName())
		if st, err := os.Stat(rf); err == nil && !st.IsDir() {
			ar.RegionFile = rf
			ar.FilePresent = true
		}
		out.Affected = append(out.Affected, ar)
	}

	sort.Slice(out.Affected, func(i, j int) bool {
		if out.Affected[i].ErrorBackups != out.Affected[j].ErrorBackups {
			return out.Affected[i].ErrorBackups > out.Affected[j].ErrorBackups
		}
		// stable tiebreak by region id
		if out.Affected[i].Region.X != out.Affected[j].Region.X {
			return out.Affected[i].Region.X < out.Affected[j].Region.X
		}
		return out.Affected[i].Region.Z < out.Affected[j].Region.Z
	})
	return out, nil
}
