package segments

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

	"github.com/jandedobbeleer/oh-my-posh/src/log"
	"github.com/jandedobbeleer/oh-my-posh/src/regex"
	"github.com/jandedobbeleer/oh-my-posh/src/segments/options"
)

type Unity struct {
	Base

	UnityVersion  string
	CSharpVersion string
}

func (u *Unity) Template() string {
	return " \ue721 {{ .UnityVersion }}{{ if .CSharpVersion }} {{ .CSharpVersion }}{{ end }} "
}

func (u *Unity) Enabled() bool {
	unityVersion, err := u.GetUnityVersion()
	if err != nil {
		log.Error(err)
		return false
	}
	if unityVersion == "" {
		return false
	}
	u.UnityVersion = unityVersion

	csharpVersion, err := u.GetCSharpVersion()
	if err != nil {
		log.Error(err)
	}
	u.CSharpVersion = csharpVersion

	return true
}

func (u *Unity) GetUnityVersion() (string, error) {
	projectDir, err := u.env.HasParentFilePath("ProjectSettings", false)
	if err != nil {
		log.Debug("no ProjectSettings parent folder found")
		return "", err
	}

	if !u.env.HasFilesInDir(projectDir.Path, "ProjectVersion.txt") {
		log.Debug("no ProjectVersion.txt file found")
		return "", err
	}

	versionFilePath := filepath.Join(projectDir.Path, "ProjectVersion.txt")
	versionFileText := u.env.FileContent(versionFilePath)

	lines := strings.SplitSeq(versionFileText, "\n")
	versionPrefix := "m_EditorVersion: "
	for line := range lines {
		if !strings.HasPrefix(line, versionPrefix) {
			continue
		}
		version := strings.TrimPrefix(line, versionPrefix)
		version = strings.TrimSpace(version)
		if version == "" {
			return "", errors.New("empty m_EditorVersion")
		}
		fIndex := strings.Index(version, "f")
		if fIndex > 0 {
			return version[:fIndex], nil
		}
		return version, nil
	}

	return "", errors.New("ProjectSettings/ProjectVersion.txt is missing m_EditorVersion")
}

const (
	csharp6  = "C# 6"
	csharp73 = "C# 7.3"
	csharp8  = "C# 8"
	csharp9  = "C# 9"
)

func (u *Unity) GetCSharpVersion() (version string, err error) {
	lastDotIndex := strings.LastIndex(u.UnityVersion, ".")
	if lastDotIndex == -1 {
		return "", errors.New("lastDotIndex")
	}
	shortUnityVersion := u.UnityVersion[0:lastDotIndex]

	var csharpVersionsByUnityVersion = map[string]string{
		"2017.1": csharp6,
		"2017.2": csharp6,
		"2017.3": csharp6,
		"2017.4": csharp6,
		"2018.1": csharp6,
		"2018.2": csharp6,
		"2018.3": csharp73,
		"2018.4": csharp73,
		"2019.1": csharp73,
		"2019.2": csharp73,
		"2019.3": csharp73,
		"2019.4": csharp73,
		"2020.1": csharp73,
		"2020.2": csharp8,
		"2020.3": csharp8,
		"2021.1": csharp8,
		"2021.2": csharp9,
		"2021.3": csharp9,
		"2022.1": csharp9,
		"2022.2": csharp9,
		"2023.1": csharp9,
		"2023.2": csharp9,
	}

	csharpVersion, found := csharpVersionsByUnityVersion[shortUnityVersion]
	if found {
		return csharpVersion, nil
	}

	log.Debug(fmt.Sprintf("Unity version %s doesn't exist in the map", shortUnityVersion))
	return u.GetCSharpVersionFromWeb(shortUnityVersion)
}

func (u *Unity) GetCSharpVersionFromWeb(shortUnityVersion string) (version string, err error) {
	url := fmt.Sprintf("https://docs.unity3d.com/%s/Documentation/Manual/CSharpCompiler.html", shortUnityVersion)
	httpTimeout := u.options.Int(options.HTTPTimeout, 2000)

	body, err := u.env.HTTPRequest(url, nil, httpTimeout)
	if err != nil {
		return "", err
	}

	pageContent := string(body)

	pattern := `<a href="https://(?:docs|learn)\.microsoft\.com/en-us/dotnet/csharp/whats-new/csharp-[0-9]+-?[0-9]*">(?P<csharpVersion>.*)</a>`
	matches := regex.FindNamedRegexMatch(pattern, pageContent)
	if matches != nil && matches["csharpVersion"] != "" {
		csharpVersion := strings.TrimSuffix(matches["csharpVersion"], ".0")
		return csharpVersion, nil
	}

	return "", nil
}
