package model

import (
	"encoding/json"
	"errors"
	"time"
)

// LibraryElement is the model for library element definitions.
type LibraryElement struct {
	ID    int64 `xorm:"pk autoincr 'id'"`
	OrgID int64 `xorm:"org_id"`
	// Deprecated: use FolderUID instead
	FolderID    int64  `xorm:"folder_id"`
	FolderUID   string `xorm:"folder_uid"`
	UID         string `xorm:"uid"`
	Name        string
	Kind        int64
	Type        string
	Description string
	Model       json.RawMessage `xorm:"TEXT"` // Column is defined as TEXT in `library_element`.
	Version     int64

	Created time.Time
	Updated time.Time

	CreatedBy int64
	UpdatedBy int64
}

// LibraryElementWithMeta is the model used to retrieve entities with additional meta information.
type LibraryElementWithMeta struct {
	ID    int64 `xorm:"pk autoincr 'id'"`
	OrgID int64 `xorm:"org_id"`
	// Deprecated: use FolderUID instead
	FolderID    int64  `xorm:"folder_id"`
	FolderUID   string `xorm:"folder_uid"`
	UID         string `xorm:"uid"`
	Name        string
	Kind        int64
	Type        string
	Description string
	Model       json.RawMessage
	Version     int64

	Created time.Time
	Updated time.Time

	FolderName          string
	ConnectedDashboards int64
	CreatedBy           int64
	UpdatedBy           int64
	CreatedByName       string
	CreatedByEmail      string
	UpdatedByName       string
	UpdatedByEmail      string
}

// LibraryElementDTO is the frontend DTO for entities.
type LibraryElementDTO struct {
	ID    int64 `json:"id"`
	OrgID int64 `json:"orgId"`
	// Deprecated: use FolderUID instead
	FolderID      int64                 `json:"folderId"`
	FolderUID     string                `json:"folderUid"`
	UID           string                `json:"uid"`
	Name          string                `json:"name"`
	Kind          int64                 `json:"kind"`
	Type          string                `json:"type"`
	Description   string                `json:"description"`
	Model         json.RawMessage       `json:"model"`
	Version       int64                 `json:"version"`
	Meta          LibraryElementDTOMeta `json:"meta"`
	SchemaVersion int64                 `json:"schemaVersion,omitempty"`
}

// LibraryElementSearchResult is the search result for entities.
type LibraryElementSearchResult struct {
	TotalCount int64               `json:"totalCount"`
	Elements   []LibraryElementDTO `json:"elements"`
	Page       int                 `json:"page"`
	PerPage    int                 `json:"perPage"`
}

// LibraryElementDTOMeta is the meta information for LibraryElementDTO.
type LibraryElementDTOMeta struct {
	FolderName          string `json:"folderName"`
	FolderUID           string `json:"folderUid"`
	ConnectedDashboards int64  `json:"connectedDashboards"`

	Created time.Time `json:"created"`
	Updated time.Time `json:"updated"`

	CreatedBy LibraryElementDTOMetaUser `json:"createdBy"`
	UpdatedBy LibraryElementDTOMetaUser `json:"updatedBy"`
}

type LibraryElementDTOMetaUser struct {
	Id        int64  `json:"id"`
	Name      string `json:"name"`
	AvatarUrl string `json:"avatarUrl"`
}

// LibraryElementConnectionDTO is the frontend DTO for element connections.
type LibraryElementConnectionDTO struct {
	// Deprecated: this field will be removed in the future
	ID            int64                     `json:"id"`
	Kind          int64                     `json:"kind"`
	ElementID     int64                     `json:"elementId"`
	ConnectionID  int64                     `json:"connectionId"`
	ConnectionUID string                    `json:"connectionUid"`
	Created       time.Time                 `json:"created"`
	CreatedBy     LibraryElementDTOMetaUser `json:"createdBy"`
}

var (
	// errLibraryElementAlreadyExists is an error for when the user tries to add a library element that already exists.
	ErrLibraryElementAlreadyExists = errors.New("library element with that name or UID already exists")
	// ErrLibraryElementNotFound is an error for when a library element can't be found.
	ErrLibraryElementNotFound = errors.New("library element could not be found")
	// errLibraryElementDashboardNotFound is an error for when a library element connection can't be found.
	ErrLibraryElementDashboardNotFound = errors.New("library element connection could not be found")
	// ErrLibraryElementHasConnections is an error for when an user deletes a library element that is connected.
	ErrLibraryElementHasConnections = errors.New("the library element has connections")
	// errLibraryElementVersionMismatch is an error for when a library element has been changed by someone else.
	ErrLibraryElementVersionMismatch = errors.New("the library element has been changed by someone else")
	// errLibraryElementUnSupportedElementKind is an error for when the kind is unsupported.
	ErrLibraryElementUnSupportedElementKind = errors.New("the element kind is not supported")
	// ErrFolderHasConnectedLibraryElements is an error for when a user deletes a folder that contains connected library elements.
	ErrFolderHasConnectedLibraryElements = errors.New("folder contains library elements that are linked in use")
	// errLibraryElementInvalidUID is an error for when the uid of a library element is invalid
	ErrLibraryElementInvalidUID = errors.New("uid contains illegal characters")
	// errLibraryElementUIDTooLong is an error for when the uid of a library element is invalid
	ErrLibraryElementUIDTooLong = errors.New("uid too long, max 40 characters")
	// ErrLibraryElementProvisionedFolder indicates that a library element cannot be created on a provisioned folder.
	ErrLibraryElementProvisionedFolder = errors.New("resource type not supported in repository-managed folders")
	// ErrLibraryElementInsufficientPermissions is returned when the caller lacks permission to perform a library element operation in a folder.
	ErrLibraryElementInsufficientPermissions = errors.New("insufficient permissions for library element operation")
)

// Commands

// CreateLibraryElementCommand is the command for adding a LibraryElement
// swagger:model
type CreateLibraryElementCommand struct {
	// ID of the folder where the library element is stored.
	//
	// Deprecated: use FolderUID instead
	FolderID int64 `json:"folderId"`
	// UID of the folder where the library element is stored.
	FolderUID *string `json:"folderUid"`
	// Name of the library element.
	Name string `json:"name"`
	// The JSON model for the library element.
	// swagger:type object
	Model json.RawMessage `json:"model"`
	// Kind of element to create, Use 1 for library panels or 2 for c.
	// Description:
	// * 1 - library panels
	// Enum: 1
	Kind int64 `json:"kind" binding:"Required"`
	// required: false
	UID string `json:"uid"`
}

// PatchLibraryElementCommand is the command for patching a LibraryElement
type PatchLibraryElementCommand struct {
	// ID of the folder where the library element is stored.
	//
	// Deprecated: use FolderUID instead
	FolderID int64 `json:"folderId" binding:"Default(-1)"`
	// UID of the folder where the library element is stored.
	FolderUID *string `json:"folderUid"`
	// Name of the library element.
	Name string `json:"name"`
	// The JSON model for the library element.
	Model json.RawMessage `json:"model,omitempty"`
	// Kind of element to create, Use 1 for library panels or 2 for c.
	// Description:
	// * 1 - library panels
	// Enum: 1
	Kind int64 `json:"kind" binding:"Required"`
	// Version of the library element you are updating.
	Version int64 `json:"version" binding:"Required"`
	// required: false
	UID string `json:"uid"`
}

// GetLibraryElementCommand is the command for getting a library element.
type GetLibraryElementCommand struct {
	FolderName string
	// Deprecated: use FolderUID instead
	FolderID int64
	UID      string
	Name     string
}

// SearchLibraryElementsQuery is the query used for searching for Elements
type SearchLibraryElementsQuery struct {
	PerPage       int
	Page          int
	SearchString  string
	SortDirection string
	Kind          int
	TypeFilter    string
	ExcludeUID    string
	// Deprecated: use FolderFilterUIDs instead
	FolderFilter     string
	FolderFilterUIDs string
}

// LibraryElementResponse is a response struct for LibraryElementDTO.
type LibraryElementResponse struct {
	Result LibraryElementDTO `json:"result"`
}

// LibraryElementSearchResponse is a response struct for LibraryElementSearchResult.
type LibraryElementSearchResponse struct {
	Result LibraryElementSearchResult `json:"result"`
}

// LibraryElementArrayResponse is a response struct for an array of LibraryElementDTO.
type LibraryElementArrayResponse struct {
	Result []LibraryElementDTO `json:"result"`
}

// LibraryElementConnectionsResponse is a response struct for an array of LibraryElementConnectionDTO.
type LibraryElementConnectionsResponse struct {
	Result []LibraryElementConnectionDTO `json:"result"`
}

// DeleteLibraryElementResponse is the response struct for deleting a library element.
type DeleteLibraryElementResponse struct {
	ID      int64  `json:"id"`
	Message string `json:"message"`
}

// LibraryElementKind is used for the kind of library element
type LibraryElementKind int

const (
	// PanelElement is used for library elements that are of the Panel kind
	PanelElement LibraryElementKind = iota + 1
)

const LibraryElementTableName = "library_element"
const LibraryElementConnectionTableName = "library_element_connection"
