package superset

import (
	"encoding/json"

	"net/http"
	"net/url"
	"strconv"

	"github.com/fiskerinc/cloud-services/pkg/logger"
	"github.com/pkg/errors"
)

func GetEmbeddableDashboards(accToken string) (embeddableDashboards []EmbeddableDashboard, err error) {
	publishedDashboards, err := getPublishedDashboards(accToken)
	if err != nil {
		return nil, err
	}
	embeddableDashboards = make([]EmbeddableDashboard, 0, len(publishedDashboards))
	for _, dashboard := range publishedDashboards {
		tempId, err := getDashboardsEmbeddedID(dashboard.ID, accToken)

		// If one of the dashboards gets an error, that could be fine, as long as not all of them do
		// The dashboard failed to have embedding data on it
		if err != nil || tempId == "" {
			continue
		}

		embeddableDashboards = append(embeddableDashboards, EmbeddableDashboard{Title: dashboard.DashboardTitle, EmbeddingId: tempId})
	}

	return embeddableDashboards, nil
}

func getPublishedDashboards(accToken string) (publishedDashboard []publishedDashboard, err error) {
	// Host already has the /api/v1
	targetURL, err := url.JoinPath(host, "dashboard")
	if err != nil {
		return nil, errors.WithStack(err)
	}
	// ulr.JoinPath with url escape the ?
	// Filter that published == true
	targetURL += "/?q=%7B%0A%20%20%22filters%22%3A%20%5B%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%22col%22%3A%20%22published%22%2C%0A%20%20%20%20%20%20%22opr%22%3A%20%22eq%22%2C%0A%20%20%20%20%20%20%22value%22%3A%20true%0A%20%20%20%20%7D%0A%20%20%5D%0A%7D"

	req, err := http.NewRequest("GET", targetURL, nil)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Add("Authorization", "Bearer "+accToken)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == 404 {
		logger.Warn().Msgf("404 while trying to access superset api, code: %s. Trying to get published dashboards\n", resp.Status)
	} else if resp.StatusCode >= 300 {
		logger.Error().Msgf("Failed to access superset api, code: %s. Trying to get published dashboards\n", resp.Status)
		return nil, errors.New("failed to access superset api")
	}
	var dashboardResponse getDashboardResponse
	err = json.NewDecoder(resp.Body).Decode(&dashboardResponse)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	publishedDashboard = dashboardResponse.Result
	return
}

func getDashboardsEmbeddedID(dashboardId int, accToken string) (embeddedId string, err error) {
	targetURL, err := url.JoinPath(host, "dashboard/", strconv.Itoa(dashboardId), "embedded")
	if err != nil {
		return "", errors.WithStack(err)
	}

	req, err := http.NewRequest("GET", targetURL, nil)
	if err != nil {
		return "", errors.WithStack(err)
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Add("Authorization", "Bearer "+accToken)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", errors.WithStack(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == 404 {
		logger.Warn().Msgf("Superset dashboard id %s is public, but is not embedable", embeddedId)
		return "", errors.New("failed to get embedable id")
	} else if resp.StatusCode >= 300 {
		logger.Error().Msgf("Failed to access superset api, code: %s. Trying to get embeded dashboard id\n", resp.Status)
		return "", errors.New("failed to access superset api")
	}

	var embeddedResponse getEmbeddedDashboardInfoResponse
	err = json.NewDecoder(resp.Body).Decode(&embeddedResponse)
	if err != nil {
		return "", errors.WithStack(err)
	}

	return embeddedResponse.Result.UUID, nil
}

type getDashboardResponse struct {
	Count  int                  `json:"count"`
	Ids    []int                `json:"ids"`
	Result []publishedDashboard `json:"result"`
}

type publishedDashboard struct {
	DashboardTitle string `json:"dashboard_title"`
	ID             int    `json:"id"`
}

type getEmbeddedDashboardInfoResponse struct {
	Result struct {
		UUID string `json:"uuid"`
	} `json:"result"`
}

type EmbeddableDashboard struct {
	Title       string `json:"title"`
	EmbeddingId string `json:"embedded_id"`
}
