package diffService

import (
	"bufio"
	"encoding/json"
	"firefly-launcher/pkg/constant"
	"firefly-launcher/pkg/hpatchz"
	"firefly-launcher/pkg/models"
	"firefly-launcher/pkg/sevenzip"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/wailsapp/wails/v3/pkg/application"
)

type DiffService struct{}

func (h *DiffService) CheckTypeHDiff(patchPath string) (bool, string, string) {
	if ok, err := sevenzip.IsFileIn7z(patchPath, "manifest"); err == nil && ok {
		return true, "manifest", ""
	}

	if ok, err := sevenzip.IsFileIn7z(patchPath, "hdifffiles.txt"); err == nil && ok {
		return true, "hdifffiles.txt", ""
	}
	if ok, err := sevenzip.IsFileIn7z(patchPath, "hdiffmap.json"); err == nil && ok {
		return true, "hdiffmap.json", ""
	}
	return false, "", "not found hdifffiles.txt or hdiffmap.json"
}

func (h *DiffService) VersionValidate(gamePath, patchPath string) (bool, string) {
	oldVersionData, err := models.ParseBinaryVersion(filepath.Join(gamePath, "StarRail_Data\\StreamingAssets\\BinaryVersion.bytes"))
	if err != nil {
		return false, err.Error()
	}

	if _, err := os.Stat(patchPath); err != nil {
		return false, err.Error()
	}

	if _, err := os.Stat(constant.TempUrl); os.IsNotExist(err) {
		if err := os.MkdirAll(constant.TempUrl, os.ModePerm); err != nil {
			return false, err.Error()
		}
	}
	if err := sevenzip.ExtractAFileFromZip(patchPath, "StarRail_Data\\StreamingAssets\\BinaryVersion.bytes", constant.TempUrl); err != nil {
		return false, err.Error()
	}

	binPath := filepath.Join(constant.TempUrl, "BinaryVersion.bytes")
	newVersionData, err := models.ParseBinaryVersion(binPath)
	if err != nil {
		return false, err.Error()
	}
	defer os.Remove(binPath)

	v := newVersionData.Subtract(*oldVersionData)
	if v != 0 && v != 1 {
		return false, fmt.Sprintf("the diff version %s not valid with game version %s", newVersionData, oldVersionData)
	}

	return true, "validated"
}

func (h *DiffService) HDiffPatchData(gamePath string) (bool, string) {
	hdiffMapPath := filepath.Join(gamePath, "hdiffmap.json")
	hdiffFilesPath := filepath.Join(gamePath, "hdifffiles.txt")

	var jsonData struct {
		DiffMap []*models.HDiffData `json:"diff_map"`
	}

	if _, err := os.Stat(hdiffMapPath); err == nil {
		data, err := os.ReadFile(hdiffMapPath)
		if err != nil {
			return false, err.Error()
		}

		var jsonDataDiffMap struct {
			DiffMap []*models.DiffMapType `json:"diff_map"`
		}
		if err := json.Unmarshal(data, &jsonDataDiffMap); err != nil {
			return false, err.Error()
		}
		for _, entry := range jsonDataDiffMap.DiffMap {
			jsonData.DiffMap = append(jsonData.DiffMap, entry.ToHDiffData())
		}
	} else if _, err := os.Stat(hdiffFilesPath); err == nil {
		files, err := models.LoadHDiffFiles(hdiffFilesPath)
		if err != nil {
			return false, err.Error()
		}
		for _, entry := range files {
			jsonData.DiffMap = append(jsonData.DiffMap, entry.ToHDiffData())
		}
	} else {
		return false, "no hdiff entries map exist"
	}
	application.Get().Event.Emit("diff:stage", map[string]string{"stage": "Patching HDiff"})
	for i, entry := range jsonData.DiffMap {
		application.Get().Event.Emit(
			"diff:progress", map[string]int{
				"progress":    i,
				"maxProgress": len(jsonData.DiffMap),
			})

		sourceFile := filepath.Join(gamePath, entry.SourceFileName)
		patchFile := filepath.Join(gamePath, entry.PatchFileName)
		targetFile := filepath.Join(gamePath, entry.TargetFileName)

		if _, err := os.Stat(patchFile); os.IsNotExist(err) {
			continue
		}

		if entry.SourceFileName == "" {
			hpatchz.ApplyPatchEmpty(patchFile, targetFile)
			os.Remove(patchFile)
			continue
		}

		if _, err := os.Stat(sourceFile); os.IsNotExist(err) {
			continue
		}

		hpatchz.ApplyPatch(sourceFile, patchFile, targetFile)
		if entry.SourceFileName != entry.TargetFileName {
			os.Remove(sourceFile)
		}
		os.Remove(patchFile)
	}

	os.Remove(filepath.Join(gamePath, "hdiffmap.json"))
	os.Remove(filepath.Join(gamePath, "hdifffiles.txt"))
	return true, "patching completed"
}

func (h *DiffService) DeleteFiles(gamePath string) (bool, string) {
	var deleteFiles []string

	file, err := os.Open(filepath.Join(gamePath, "deletefiles.txt"))
	if err != nil {
		return false, ""
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line != "" {
			deleteFiles = append(deleteFiles, line)
		}
	}

	if err := scanner.Err(); err != nil {
		return false, "no delete files exist"
	}

	for i, file := range deleteFiles {
		os.Remove(filepath.Join(gamePath, file))
		application.Get().Event.Emit("diff:progress", map[string]int{"progress": i, "maxProgress": len(deleteFiles)})
	}
	os.Remove(filepath.Join(gamePath, "deletefiles.txt"))
	return true, ""
}
