package grafana

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"time"

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

type Dashboard struct {
	IsStarred              bool
	Type                   string
	CanSave                bool
	CanEdit                bool
	CanAdmin               bool
	CanStar                bool
	CanDelete              bool
	Slug                   string
	URL                    string
	Expires                time.Time
	Created                time.Time
	Updated                time.Time
	UpdatedBy              string
	CreatedBy              string
	Version                uint
	HasACL                 bool
	IsFolder               bool
	FolderID               uint
	FolderUID              string
	FolderTitle            string
	FolderURL              string
	Provisioned            bool
	ProvisionedExternalID  string
	AnnotationsPermissions AnnotationsPermissions
	Dashboard              any
}

type AnnotationsPermissions struct {
	Dashboard    AnnotationPermissions
	Organization AnnotationPermissions
}

type AnnotationPermissions struct {
	CanAdd    bool
	CanEdit   bool
	CanDelete bool
}

type DashboardClient struct {
	client *Client
}

func (c *DashboardClient) Get(ctx context.Context, uid string) (*Dashboard, *Response, error) {
	req, err := c.client.newRequest(ctx, "GET", fmt.Sprintf("/dashboards/uid/%s", uid), nil)
	if err != nil {
		return nil, nil, err
	}

	var body schema.Dashboard

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

	return DashboardFromSchema(body), resp, nil
}

type DashboardCreateOpts struct {
	Dashboard any
	FolderID  uint
	FolderUID string
	Message   string
	Overwrite bool
}

func (o DashboardCreateOpts) Validate() error {
	if o.Dashboard == nil {
		return errors.New("dashboard is nil")
	}
	return nil
}

type DashboardCreateResponse struct {
	DashboardID  uint
	DashboardUID string
	URL          string
	Status       string
	Version      uint
	Slug         string
}

func (c *DashboardClient) Create(
	ctx context.Context,
	opts DashboardCreateOpts,
) (*DashboardCreateResponse, *Response, error) {
	if err := opts.Validate(); err != nil {
		return nil, nil, err
	}

	var reqBody schema.DashboardCreateRequest
	reqBody.Dashboard = opts.Dashboard
	reqBody.Overwrite = opts.Overwrite
	reqBody.Message = opts.Message
	if opts.FolderUID != "" {
		reqBody.FolderUID = opts.FolderUID
	} else if opts.FolderID > 0 {
		reqBody.FolderID = opts.FolderID
	}

	reqBodyData, err := json.Marshal(reqBody)
	if err != nil {
		return nil, nil, err
	}

	req, err := c.client.newRequest(ctx, "POST", "/dashboards/db", bytes.NewReader(reqBodyData))
	if err != nil {
		return nil, nil, err
	}

	var respBody schema.DashboardCreateResponse
	resp, err := c.client.do(req, &respBody)
	if err != nil {
		return nil, resp, err
	}

	return DashboardCreateResponseFromSchema(respBody), resp, nil
}

func (c *DashboardClient) Delete(ctx context.Context, uid string) (*Response, error) {
	req, err := c.client.newRequest(ctx, "DELETE", fmt.Sprintf("/dashboards/uid/%s", uid), nil)
	if err != nil {
		return nil, err
	}

	return c.client.do(req, nil)
}

func (c *DashboardClient) ParseRaw(raw []byte) (*Dashboard, error) {
	var body schema.Dashboard
	if err := json.Unmarshal(raw, &body); err != nil {
		return nil, err
	}

	return DashboardFromSchema(body), nil
}