package grafana

import (
	"context"
	"fmt"
	"net/url"
	"strconv"
	"time"

	"git.ar21.de/yolokube/grafana-backuper/pkg/grafana/schema"
)

type DashboardVersionParam func(*url.Values)

func WithLimit(limit uint) DashboardVersionParam {
	return func(v *url.Values) {
		v.Set("limit", strconv.FormatUint(uint64(limit), 10))
	}
}

func WithStart(start uint) DashboardVersionParam {
	return func(v *url.Values) {
		v.Set("start", strconv.FormatUint(uint64(start), 10))
	}
}

type DashboardVersion struct {
	ID            uint
	DashboardID   uint
	DashboardUID  string
	ParentVersion uint
	RestoredFrom  uint
	Version       uint
	Created       time.Time
	CreatedBy     string
	Message       string
	Data          any
}

type DashboardVersionClient struct {
	client *Client
}

func (c *DashboardVersionClient) GetByID(
	ctx context.Context,
	id, version uint,
	params ...DashboardVersionParam,
) (*DashboardVersion, *Response, error) {
	dashboardVersionURL, err := url.Parse(fmt.Sprintf("/dashboards/id/%d/versions/%d", id, version))
	if err != nil {
		return nil, nil, err
	}

	return c.get(ctx, dashboardVersionURL, params...)
}

func (c *DashboardVersionClient) GetByUID(
	ctx context.Context,
	uid string,
	version uint,
	params ...DashboardVersionParam,
) (*DashboardVersion, *Response, error) {
	dashboardVersionURL, err := url.Parse(fmt.Sprintf("/dashboards/uid/%s/versions/%d", uid, version))
	if err != nil {
		return nil, nil, err
	}

	return c.get(ctx, dashboardVersionURL, params...)
}

func (c *DashboardVersionClient) Get(
	ctx context.Context,
	input string,
	version uint,
	params ...DashboardVersionParam,
) (*DashboardVersion, *Response, error) {
	if id, err := strconv.ParseUint(input, 10, 64); err == nil {
		return c.GetByID(ctx, uint(id), version, params...)
	}
	return c.GetByUID(ctx, input, version, params...)
}

func (c *DashboardVersionClient) ListByID(
	ctx context.Context,
	id uint,
	params ...DashboardVersionParam,
) ([]*DashboardVersion, *Response, error) {
	dashboardVersionURL, err := url.Parse(fmt.Sprintf("/dashboards/id/%d/versions", id))
	if err != nil {
		return nil, nil, err
	}

	return c.list(ctx, dashboardVersionURL, params...)
}

func (c *DashboardVersionClient) ListByUID(
	ctx context.Context,
	uid string,
	params ...DashboardVersionParam,
) ([]*DashboardVersion, *Response, error) {
	dashboardVersionURL, err := url.Parse(fmt.Sprintf("/dashboards/uid/%s/versions", uid))
	if err != nil {
		return nil, nil, err
	}

	return c.list(ctx, dashboardVersionURL, params...)
}

func (c *DashboardVersionClient) List(
	ctx context.Context,
	input string,
	params ...DashboardVersionParam,
) ([]*DashboardVersion, *Response, error) {
	if id, err := strconv.ParseUint(input, 10, 64); err == nil {
		return c.ListByID(ctx, uint(id), params...)
	}
	return c.ListByUID(ctx, input, params...)
}

func (c *DashboardVersionClient) get(
	ctx context.Context,
	dashboardVersionURL *url.URL,
	params ...DashboardVersionParam,
) (*DashboardVersion, *Response, error) {
	if len(params) > 0 {
		query := dashboardVersionURL.Query()

		for _, param := range params {
			param(&query)
		}

		dashboardVersionURL.RawQuery = query.Encode()
	}

	req, err := c.client.newRequest(ctx, "GET", dashboardVersionURL.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var body schema.DashboardVersion

	resp, err := c.client.do(req, &body)
	if err != nil {
		return nil, resp, err
	}

	return DashboardVersionFromSchema(body), resp, nil
}

func (c *DashboardVersionClient) list(
	ctx context.Context,
	dashboardVersionURL *url.URL,
	params ...DashboardVersionParam,
) ([]*DashboardVersion, *Response, error) {
	if len(params) > 0 {
		query := dashboardVersionURL.Query()

		for _, param := range params {
			param(&query)
		}

		dashboardVersionURL.RawQuery = query.Encode()
	}

	req, err := c.client.newRequest(ctx, "GET", dashboardVersionURL.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var body schema.DashboardVersionListResponse

	resp, err := c.client.do(req, &body)
	if err != nil {
		return nil, resp, err
	}

	dashboardVersions := make([]*DashboardVersion, 0, len(body))
	for _, dashboardVersion := range body {
		dashboardVersions = append(dashboardVersions, DashboardVersionFromSchema(dashboardVersion))
	}

	return dashboardVersions, resp, nil
}