// The file implements the Entity interface on the ComponentDefinition struct.
// This is not autogenerated.
package component

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/gofrs/uuid"
	"github.com/meshery/meshkit/database"
	"github.com/meshery/meshkit/models/meshmodel/entity"
	"github.com/meshery/meshkit/utils"
	core "github.com/meshery/schemas/models/core"
	"gorm.io/gorm/clause"
)

type Styles = core.ComponentStyles

func (c ComponentDefinition) TableName() string {
	return "component_definition_dbs"
}

func (c ComponentDefinition) Type() entity.EntityType {
	return entity.ComponentDefinition
}

func (c *ComponentDefinition) GenerateID() (uuid.UUID, error) {
	return uuid.NewV4()
}

func (c ComponentDefinition) GetID() uuid.UUID {
	return c.ID
}

func (c *ComponentDefinition) GetEntityDetail() string {
	if c.Model == nil {
		return fmt.Sprintf("type: %s, definition version: %s, name: %s", c.Type(), c.Version, c.DisplayName)
	}

	return fmt.Sprintf("type: %s, definition version: %s, name: %s, model: %s, version: %s", c.Type(), c.Version, c.DisplayName, c.Model.Name, c.Model.Version)
}

func (c *ComponentDefinition) Create(db *database.Handler, hostID uuid.UUID) (uuid.UUID, error) {
	generatedID, err := c.GenerateID()
	if err != nil {
		return uuid.Nil, err
	}
	c.ID = generatedID

	isAnnotation := c.Metadata.IsAnnotation

	if c.Component.Schema == "" && !isAnnotation { //For components which has an empty schema and is not an annotation, return error
		// return ErrEmptySchema()
		return uuid.Nil, nil
	}

	if c.Model == nil {
		return uuid.Nil, fmt.Errorf("component model is nil for component %q (%s %s)", c.DisplayName, c.Component.Kind, c.Version)
	}

	mid, err := c.Model.Create(db, hostID)
	if err != nil {
		return uuid.UUID{}, err
	}

	if !utils.IsSchemaEmpty(c.Component.Schema) {
		if c.Metadata.AdditionalProperties == nil {
			c.Metadata.AdditionalProperties = make(map[string]interface{})
		}
		c.Metadata.AdditionalProperties["hasInvalidSchema"] = true
	}

	modelID := core.Uuid(mid)
	c.ModelID = &modelID
	err = db.Omit(clause.Associations).Create(&c).Error
	return c.ID, err
}

func (m *ComponentDefinition) UpdateStatus(db *database.Handler, status entity.EntityStatus) error {
	return nil
}

func (c ComponentDefinition) WriteComponentDefinition(componentDirPath string, fileType string) (bool, error) {
	if c.Component.Kind == "" {
		return false, nil
	}
	componentPath := filepath.Join(componentDirPath, c.Component.Kind+"."+fileType)
	if _, err := os.Stat(componentPath); err != nil {
		if fileType == "yaml" {
			err := utils.WriteYamlToFile[ComponentDefinition](componentPath, c)
			return false, err
		}
		err := utils.WriteJSONToFile[ComponentDefinition](componentPath, c)
		return false, err
	}
	return true, nil
}

func (c *ComponentDefinition) ReplaceSVGData(baseDir string) error {
	// Styles is a *core.ComponentStyles; if it is nil there is nothing to
	// replace and we return cleanly instead of panicking on a nil deref (a
	// latent bug in the v1beta1 helper this was ported from).
	if c.Styles == nil {
		return nil
	}

	compStyle := c.Styles
	if compStyle.SvgColor != "" {
		svgColor, err := utils.ReadSVGData(baseDir, compStyle.SvgColor)
		if err == nil {
			compStyle.SvgColor = svgColor
		} else {
			return err
		}
	}
	if compStyle.SvgWhite != "" {
		svgWhite, err := utils.ReadSVGData(baseDir, compStyle.SvgWhite)
		if err == nil {
			compStyle.SvgWhite = svgWhite
		} else {
			return err
		}
	}
	if compStyle.SvgComplete != "" {
		svgComplete, err := utils.ReadSVGData(baseDir, compStyle.SvgComplete)
		if err == nil {
			compStyle.SvgComplete = svgComplete
		} else {
			return err
		}
	}
	c.Styles = compStyle
	return nil
}
