package internal

import (
	"bufio"
	"encoding/json"
	"firefly-launcher/pkg/constant"
	"firefly-launcher/pkg/models"
	"firefly-launcher/pkg/sevenzip"
	"firefly-launcher/pkg/verifier"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"

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

type HdiffzService struct{}

func (h *HdiffzService) 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 *HdiffzService) DataExtract(gamePath, patchPath string) (bool, string) {
	if _, err := os.Stat(gamePath); 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.ExtractAllFilesFromZip(patchPath, constant.TempUrl); err != nil {
		os.RemoveAll(constant.TempUrl)
		return false, err.Error()
	}

	validator, err := verifier.NewVerifier(gamePath, constant.TempUrl)
	if err != nil {
		os.RemoveAll(constant.TempUrl)
		return false, err.Error()
	}

	if err := validator.VerifyAll(); err != nil {
		os.RemoveAll(constant.TempUrl)
		return false, err.Error()
	}

	return true, "validated"
}

func (h *HdiffzService) CutData(gamePath string) (bool, string) {
	if _, err := os.Stat(constant.TempUrl); os.IsNotExist(err) {
		return false, err.Error()
	}

	err := filepath.Walk(constant.TempUrl, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		relPath, err := filepath.Rel(constant.TempUrl, path)
		if err != nil {
			return err
		}
		destPath := filepath.Join(gamePath, relPath)
		application.Get().EmitEvent("hdiffz:message", map[string]string{"message": destPath})
		if info.IsDir() {
			return os.MkdirAll(destPath, os.ModePerm)
		}

		if err := os.MkdirAll(filepath.Dir(destPath), os.ModePerm); err != nil {
			return err
		}

		if err := os.Rename(path, destPath); err != nil {
			srcFile, err := os.Open(path)
			if err != nil {
				return err
			}
			defer srcFile.Close()

			dstFile, err := os.Create(destPath)
			if err != nil {
				return err
			}
			defer dstFile.Close()

			if _, err := io.Copy(dstFile, srcFile); err != nil {
				return err
			}
			_ = os.Remove(path)
		}
		return nil
	})

	if err != nil {
		return false, err.Error()
	}

	_ = os.RemoveAll(constant.TempUrl)

	return true, "cut completed"
}

func (h *HdiffzService) PatchData(gamePath string) (bool, string) {
	data, err := os.ReadFile(filepath.Join(gamePath, "hdiffmap.json"))
	if err != nil {
		return false, err.Error()
	}

	var jsonData struct {
		DiffMap []*models.DiffMapType `json:"diff_map"`
	}
	if err := json.Unmarshal(data, &jsonData); err != nil {
		return false, err.Error()
	}

	for i, entry := range jsonData.DiffMap {
		application.Get().EmitEvent(
			"hdiffz: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(sourceFile); os.IsNotExist(err) {
			continue
		}
		if _, err := os.Stat(patchFile); os.IsNotExist(err) {
			continue
		}

		cmd := exec.Command(constant.ToolHPatchzExe.String(), sourceFile, patchFile, targetFile)
		cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
		_, err := cmd.CombinedOutput()
		if err != nil {
			continue
		}
	}

	return true, "patching completed"
}

func (h *HdiffzService) 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, ""
	}

	for i, file := range deleteFiles {
		os.Remove(filepath.Join(gamePath, file))
		application.Get().EmitEvent("hdiffz:progress", map[string]int{"progress": i, "maxProgress": len(deleteFiles)})
	}

	return true, ""
}
