package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"

	"github.com/inconshreveable/go-update"
	wruntime "github.com/wailsapp/wails/v2/pkg/runtime"
)

const client_id string = "9305aeb8-5ecb-4e7a-b28f-c33aefcfbd8d"
const client_version string = "0.0.5"

type LauncherMetadata struct {
	Schema_Version string
	Version        string
	Desc           string
	Downloads      map[string]string
}

// App struct
type App struct {
	Ctx           context.Context
	PrismLauncher Prism
	Java          JavaManager
	Instance      InstanceManager
	Modpacks      ModpackManager
	Auth          authenticationResp
}

// NewApp creates a new App application struct
func NewApp() *App {
	a := &App{}
	a.Java = JavaManager{app: a}
	a.Instance = InstanceManager{app: a}
	a.Modpacks = ModpackManager{app: a}
	return a
}

// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
	a.Ctx = ctx
}

// Greet returns a greeting for the given name

func openbrowser(url string) {
	var err error

	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", url).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
	case "darwin":
		err = exec.Command("open", url).Start()
	default:
		err = fmt.Errorf("unsupported platform")
	}
	if err != nil {
		log.Fatal(err)
	}

}

func (a *App) CheckPrerequisites() {
	buff := new(bytes.Buffer)
	err := HttpDownload("launcher.json", buff, nil)
	fmt.Printf("Starting\n")
	if err == nil {
		data, _ := io.ReadAll(buff)
		meta := LauncherMetadata{}
		json.Unmarshal(data, &meta)
		if client_version != meta.Version {
			//Update available!
			val, _ := wruntime.MessageDialog(a.Ctx, wruntime.MessageDialogOptions{Type: wruntime.QuestionDialog, Title: "Update!", Message: fmt.Sprintf("There is an update available:\n\n%s -> %s\n\nUpdate Description:\n\n%s\n\nWould you like to update?\n", client_version, meta.Version, meta.Desc)})
			if val == "Yes" {
				//run the update
				fmt.Printf("Updating\n")
				buff := new(bytes.Buffer)
				HttpDownload(meta.Downloads[runtime.GOOS], buff, nil)
				executable, _ := os.Executable()
				err := update.Apply(buff, update.Options{})
				if err != nil {
					fmt.Printf("Error!")
				}
				child := exec.Command(executable)
				err = child.Start()
				if err != nil {
					fmt.Printf("Unable to launch: %s\n", err)
				}
				wruntime.Quit(a.Ctx)
			}
		}
	}

	a.Status("Querrying Existing Instances")
	a.Instance.SearchInstances()
	a.Status("Pulling Modpacks")
	a.Modpacks.QuerryModpacks()
	a.Status("Logging in with Microsoft")
	dir, _ := os.UserConfigDir()
	authenticated := false
	if _, err := os.Stat(filepath.Join(dir, "FCLauncher", "authentication.json")); err == nil {
		f, _ := os.OpenFile(filepath.Join(dir, "FCLauncher", "authentication.json"), os.O_RDONLY, 0755)
		defer f.Close()
		data, _ := io.ReadAll(f)
		json.Unmarshal(data, &a.Auth)
		a.Auth, err = TokenRefresh(*a, a.Auth)
		if err == nil {
			authenticated = true
		} else {
			fmt.Printf("token reauth failed, requesting device code: %s\n", err)
		}
	}
	if !authenticated {
		var err error
		//a.Auth, err = AuthCode(*a)
		a.Auth, err = OAuth2(*a)
		if err != nil {
			fmt.Printf("Authentication Error: %s\n", err)
			return
		}
	}
	os.MkdirAll(filepath.Join(dir, "FCLauncher"), 0755)
	f, _ := os.OpenFile(filepath.Join(dir, "FCLauncher", "authentication.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0755)
	defer f.Close()
	data, _ := json.Marshal(a.Auth)
	f.Write(data)
}

func (App) GetVersions() ([]string, error) {
	manifest, err := GetVersionManifest()
	if err != nil {
		fmt.Printf("Manifest Error: %s\n", err)
		return []string{}, err
	}
	versions := []string{}
	for _, version := range manifest.Versions {
		versions = append(versions, version.Id)
	}
	return versions, nil

}

func (a *App) Status(status string) {
	fmt.Printf("LOG: %s\n", status)
	wruntime.EventsEmit(a.Ctx, "status", status)
}
