// SPDX-License-Identifier: MIT

package filesystem

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

	"code.forgejo.org/f3/gof3/v3/tree/generic"
	"code.forgejo.org/f3/gof3/v3/util"
)

func Inverse(from, to string) {
	CopyIDs(from, to)
	InverseRename(from, to)
}

func InverseRename(from, to string) {
	fromRename := buildRenameFilename(from)
	if !util.FileExists(fromRename) {
		return
	}
	var fromRenameMap generic.TreeRenameMap
	loadJSON(fromRename, &fromRenameMap)
	toRenameMap := make(generic.TreeRenameMap, len(fromRenameMap))
	for kind, fromRenames := range fromRenameMap {
		toRenames := make(map[string]string, len(fromRenames))
		for from, to := range fromRenames {
			toRenames[to] = from
		}
		toRenameMap[kind] = toRenames
	}
	saveJSON(buildRenameFilename(to), toRenameMap)
}

func CopyIDs(from, to string) {
	mappedID := copyDirectoryIDs(from, to)

	for _, entry := range util.ReadDir(to) {
		switch entry.Name() {
		case "repositories", "objects":
			continue
		}
		if !entry.Type().IsDir() {
			continue
		}
		toDir := filepath.Join(to, entry.Name())
		toID := entry.Name()
		fromID, has := mappedID[toID]
		if !has {
			fromID = toID
		}
		fromDir := filepath.Join(from, fromID)
		if !util.IsDir(fromDir) {
			var extra string
			if has {
				extra = fmt.Sprintf(" (toID %s mapped to fromID %s)", toID, fromID)
			}
			panic(fmt.Errorf("fromDir %s does not exist to match toDir %s%s", fromDir, toDir, extra))
		}
		CopyIDs(fromDir, toDir)
	}
}

func copyDirectoryIDs(from, to string) map[string]string {
	mappedID := collectMappedID(from)

	for _, entry := range util.ReadDir(to) {
		if !strings.HasSuffix(entry.Name(), ".json") {
			continue
		}
		toID := strings.TrimSuffix(entry.Name(), ".json")
		fromID, has := mappedID[toID]
		if !has {
			continue
		}
		idFile := filepath.Join(to, toID+".id")
		err := os.WriteFile(idFile, []byte(fromID), 0o644)
		if err != nil {
			panic(fmt.Errorf("WriteFile(%s): %w", idFile, err))
		}
	}

	return mappedID
}

func collectMappedID(from string) map[string]string {
	mappedID := make(map[string]string, 10)
	for _, entry := range util.ReadDir(from) {
		if !strings.HasSuffix(entry.Name(), ".id") {
			continue
		}
		fromID := strings.TrimSuffix(entry.Name(), ".id")
		jsonFile := filepath.Join(from, fromID+".json")
		if !util.FileExists(jsonFile) {
			panic(fmt.Errorf("missing json file %s", jsonFile))
		}
		idFile := filepath.Join(from, entry.Name())
		toID, err := os.ReadFile(idFile)
		if err != nil {
			panic(fmt.Errorf("ReadFile(%s): %w", idFile, err))
		}
		mappedID[string(toID)] = fromID
	}
	return mappedID
}
