package manager

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/hashicorp/go-version"
)

var (
	httpClient = http.Client{Timeout: 10 * time.Second}
)

type grafanaNetPlugin struct {
	Slug    string `json:"slug"`
	Version string `json:"version"`
}

type gitHubLatest struct {
	Stable  string `json:"stable"`
	Testing string `json:"testing"`
}

func (pm *PluginManager) getAllExternalPluginSlugs() string {
	var result []string
	for _, plug := range pm.plugins {
		if plug.IsCorePlugin {
			continue
		}

		result = append(result, plug.Id)
	}

	return strings.Join(result, ",")
}

func (pm *PluginManager) checkForUpdates() {
	if !pm.Cfg.CheckForUpdates {
		return
	}

	pm.log.Debug("Checking for updates")

	pluginSlugs := pm.getAllExternalPluginSlugs()
	resp, err := httpClient.Get("https://grafana.com/api/plugins/versioncheck?slugIn=" + pluginSlugs + "&grafanaVersion=" + setting.BuildVersion)
	if err != nil {
		log.Tracef("Failed to get plugins repo from grafana.com, %v", err.Error())
		return
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			log.Warn("Failed to close response body", "err", err)
		}
	}()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Tracef("Update check failed, reading response from grafana.com, %v", err.Error())
		return
	}

	gNetPlugins := []grafanaNetPlugin{}
	err = json.Unmarshal(body, &gNetPlugins)
	if err != nil {
		log.Tracef("Failed to unmarshal plugin repo, reading response from grafana.com, %v", err.Error())
		return
	}

	for _, plug := range pm.Plugins() {
		for _, gplug := range gNetPlugins {
			if gplug.Slug == plug.Id {
				plug.GrafanaNetVersion = gplug.Version

				plugVersion, err1 := version.NewVersion(plug.Info.Version)
				gplugVersion, err2 := version.NewVersion(gplug.Version)

				if err1 != nil || err2 != nil {
					plug.GrafanaNetHasUpdate = plug.Info.Version != plug.GrafanaNetVersion
				} else {
					plug.GrafanaNetHasUpdate = plugVersion.LessThan(gplugVersion)
				}
			}
		}
	}

	resp2, err := httpClient.Get("https://raw.githubusercontent.com/grafana/grafana/main/latest.json")
	if err != nil {
		log.Tracef("Failed to get latest.json repo from github.com: %v", err.Error())
		return
	}
	defer func() {
		if err := resp2.Body.Close(); err != nil {
			pm.log.Warn("Failed to close response body", "err", err)
		}
	}()
	body, err = ioutil.ReadAll(resp2.Body)
	if err != nil {
		log.Tracef("Update check failed, reading response from github.com, %v", err.Error())
		return
	}

	var latest gitHubLatest
	err = json.Unmarshal(body, &latest)
	if err != nil {
		log.Tracef("Failed to unmarshal github.com latest, reading response from github.com: %v", err.Error())
		return
	}

	if strings.Contains(setting.BuildVersion, "-") {
		pm.grafanaLatestVersion = latest.Testing
		pm.grafanaHasUpdate = !strings.HasPrefix(setting.BuildVersion, latest.Testing)
	} else {
		pm.grafanaLatestVersion = latest.Stable
		pm.grafanaHasUpdate = latest.Stable != setting.BuildVersion
	}

	currVersion, err1 := version.NewVersion(setting.BuildVersion)
	latestVersion, err2 := version.NewVersion(pm.grafanaLatestVersion)
	if err1 == nil && err2 == nil {
		pm.grafanaHasUpdate = currVersion.LessThan(latestVersion)
	}
}
