package db

import (
	"encoding/json"
	"time"
)

type mockDB struct {
	templates                 []TemplateRow
	templateValues            []TemplateValueRow
	defaultTemplates          []DefaultTemplateRow
	clusterConfigInfo         []ClusterConfigInfoRow
	clusterConfigParams       []ClusterConfigParamRow
	clusterConfigTexts        []ClusterConfigTextRow
	tenantInitialCustomParams []TenantInitialCustomParamRow
	clusterComponents         []ClusterComponentRow
	componentImages           []ComponentImageRow
	defaultVersions           []DefaultVersionRow
	auditLogs                 []AuditLogRow
}

func NewMockDB() DB {
	return &mockDB{
		templates: []TemplateRow{{}}, // avoid insert template 0
	}
}

func (db *mockDB) Begin() (DB, error) {
	return db, nil
}

func (db *mockDB) Rollback() error {
	return nil
}

func (db *mockDB) Commit() error {
	return nil
}

func (db *mockDB) CreateTemplateRow(templateID *int64, clusterType, configType, templateName, tags, contentHash string) (int64, error) {
	id := int64(len(db.templates))
	if templateID != nil {
		if *templateID >= id {
			id = *templateID
		} else {
			return 0, ErrAlreadyExists
		}
	}
	db.templates = append(db.templates, TemplateRow{
		ID:           id,
		ClusterType:  clusterType,
		ConfigType:   configType,
		TemplateName: templateName,
		Tags:         &tags,
		ContentHash:  &contentHash,
	})
	return id, nil
}

func (db *mockDB) GetTemplateRow(id int64) (TemplateRow, error) {
	for _, t := range db.templates {
		if t.ID == id {
			return t, nil
		}
	}
	return TemplateRow{}, ErrNotFound
}

func (db *mockDB) ListTemplatesByHash(clusterType, configType, contentHash string) ([]TemplateRow, error) {
	var result []TemplateRow
	for _, t := range db.templates {
		if t.ClusterType == clusterType && t.ConfigType == configType && t.ContentHash != nil && *t.ContentHash == contentHash && t.DeleteTime == nil {
			result = append(result, t)
		}
	}
	return result, nil
}

func (db *mockDB) ListTemplateRows(clusterType, configType string, tagFilter map[string]string, templateName string, pageSize, page int32) ([]TemplateRow, error) {
	var result []TemplateRow
	for _, row := range db.templates {
		var tags map[string]string
		if row.Tags != nil {
			err := json.Unmarshal([]byte(*row.Tags), &tags)
			if err != nil {
				return result, err
			}
		}
		if matchtag(tags, tagFilter) &&
			(clusterType == "" || row.ClusterType == clusterType) &&
			(configType == "" || configType == row.ConfigType) &&
			(templateName == "" || templateName == row.TemplateName) &&
			row.DeleteTime == nil {
			result = append(result, row)
		}
	}
	if pageSize > 0 {
		if page <= 0 {
			page = 1
		}
		offset := int(int64(pageSize) * int64(page-1))
		if offset >= len(result) {
			return nil, nil
		}
		end := offset + int(pageSize)
		if end > len(result) {
			end = len(result)
		}
		result = result[offset:end]
	}
	return result, nil
}

func (db *mockDB) CountTemplateRows(clusterType, configType string, tagFilter map[string]string, templateName string) (int64, error) {
	rows, err := db.ListTemplateRows(clusterType, configType, tagFilter, templateName, 0, 0)
	if err != nil {
		return 0, err
	}
	return int64(len(rows)), nil
}

func matchtag(tags, filter map[string]string) bool {
	for k, v := range filter {
		if tags[k] != v {
			return false
		}
	}
	return true
}

func (db *mockDB) DeleteTemplateRow(id int64) error {
	for i, t := range db.templates {
		if t.ID == id {
			db.templates = append(db.templates[:i], db.templates[i+1:]...)
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) InsertTemplateValueRows(rows []TemplateValueRow) error {
	for _, row := range rows {
		row.ID = int64(len(db.templateValues))
		db.templateValues = append(db.templateValues, row)
	}
	return nil
}

func (db *mockDB) ListTemplateValueRows(templateID int64) ([]TemplateValueRow, error) {
	var result []TemplateValueRow
	for _, v := range db.templateValues {
		if v.TemplateID == templateID {
			result = append(result, v)
		}
	}
	return result, nil
}

func (db *mockDB) DeleteTemplateValueRows(templateID int64) error {
	for i, v := range db.templateValues {
		if v.TemplateID == templateID {
			db.templateValues = append(db.templateValues[:i], db.templateValues[i+1:]...)
		}
	}
	return nil
}

func (db *mockDB) GetDefaultTemplate(clusterType, configType, version, cloud, instance, region, tenant_id string) (int64, error) {
	for _, template := range db.defaultTemplates {
		if template.ClusterType == clusterType &&
			template.ConfigType == configType &&
			template.Version == version &&
			template.CloudProvider == cloud &&
			template.NodeInstance == instance &&
			template.Region == region &&
			template.TenantID == tenant_id {
			return template.TemplateID, nil
		}
	}
	return 0, ErrNotFound
}

func (db *mockDB) CountDefaultTemplateByTemplateID(templateID int64) (int64, error) {
	var count int64
	for _, row := range db.defaultTemplates {
		if row.TemplateID == templateID {
			count++
		}
	}
	return count, nil
}

func (db *mockDB) ListDefaultTemplateByTemplate(clusterType, configType string, templateID int64) ([]DefaultTemplateRow, error) {
	var result []DefaultTemplateRow
	for _, row := range db.defaultTemplates {
		if row.ClusterType == clusterType && row.ConfigType == configType && row.TemplateID == templateID {
			result = append(result, row)
		}
	}
	return result, nil
}

func (db *mockDB) ListDefaultTemplateBindings(clusterType string, configType, version, cloud, instance, tenantID string, _ int32, _ int32, exactMatch bool) ([]DefaultTemplateRow, error) {
	var result []DefaultTemplateRow
	for _, row := range db.defaultTemplates {
		if row.ClusterType == clusterType &&
			(configType == "" || configType == row.ConfigType) &&
			(version == "" || version == row.Version) &&
			(cloud == "" || cloud == row.CloudProvider) &&
			(instance == "" || instance == row.NodeInstance) &&
			(tenantID == "" || tenantID == row.TenantID) {
			result = append(result, row)
		}
	}
	return result, nil
}

func (db *mockDB) CountDefaultTemplateBindings(clusterType string, configType, version, cloud, instance, tenantID string, exactMatch bool) (int64, error) {
	return int64(len(db.defaultTemplates)), nil
}

func (db *mockDB) InsertDefaultTemplate(clusterType, configType, version, cloud, instance, region, tenantID string, templateID int64) error {
	db.defaultTemplates = append(db.defaultTemplates, DefaultTemplateRow{
		ClusterType:   clusterType,
		ConfigType:    configType,
		Version:       version,
		CloudProvider: cloud,
		NodeInstance:  instance,
		Region:        region,
		TenantID:      tenantID,
		TemplateID:    templateID,
	})
	return nil
}

func (db *mockDB) UpdateDefaultTemplate(clusterType, configType, version, cloud, instance, region, tenantID string, templateID int64) error {
	for i, t := range db.defaultTemplates {
		if t.ClusterType == clusterType &&
			t.ConfigType == configType &&
			t.Version == version &&
			t.CloudProvider == cloud &&
			t.NodeInstance == instance &&
			t.Region == region &&
			t.TenantID == tenantID {
			db.defaultTemplates[i].TemplateID = templateID
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) DeleteDefaultTemplate(clusterType, configType, version, cloud, instance, region, tenantID string) error {
	for i, t := range db.defaultTemplates {
		if t.ClusterType == clusterType &&
			t.ConfigType == configType &&
			t.Version == version &&
			t.CloudProvider == cloud &&
			t.NodeInstance == instance &&
			t.Region == region &&
			t.TenantID == tenantID {
			db.defaultTemplates = append(db.defaultTemplates[:i], db.defaultTemplates[i+1:]...)
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) InsertTenantInitialCustomParams(clusterType, configType string, tenantID uint64, items []ConfigurationItem) error {
	for _, item := range items {
		db.tenantInitialCustomParams = append(db.tenantInitialCustomParams, TenantInitialCustomParamRow{
			ID:                int64(len(db.tenantInitialCustomParams)),
			ClusterType:       clusterType,
			TenantID:          tenantID,
			ConfigType:        configType,
			ConfigurationItem: item,
		})
	}
	return nil
}

func (db *mockDB) ListTenantInitialCustomParams(clusterType, configType string, tenantID uint64) ([]TenantInitialCustomParamRow, error) {
	result := []TenantInitialCustomParamRow{}
	for _, row := range db.tenantInitialCustomParams {
		if row.ClusterType == clusterType && row.TenantID == tenantID && row.ConfigType == configType {
			result = append(result, row)
		}
	}
	return result, nil
}

func (db *mockDB) ListAllTenantInitialCustomParams(clusterType string) ([]TenantInitialCustomParamRow, error) {
	result := []TenantInitialCustomParamRow{}
	for _, row := range db.tenantInitialCustomParams {
		if row.ClusterType == clusterType {
			result = append(result, row)
		}
	}
	return result, nil
}

func (db *mockDB) ListTenantInitialConfigTypes(clusterType string, tenantID uint64) ([]string, error) {
	types := make(map[string]struct{})
	for _, row := range db.tenantInitialCustomParams {
		if row.ClusterType == clusterType && row.TenantID == tenantID {
			types[row.ConfigType] = struct{}{}
		}
	}

	var result []string
	for t := range types {
		result = append(result, t)
	}
	return result, nil
}

func (db *mockDB) DeleteTenantInitialCustomParams(clusterType, configType string, tenantID uint64) error {
	for i, row := range db.tenantInitialCustomParams {
		if row.ClusterType == clusterType && row.TenantID == tenantID && row.ConfigType == configType {
			db.tenantInitialCustomParams = append(db.tenantInitialCustomParams[:i], db.tenantInitialCustomParams[i+1:]...)
			return nil
		}
	}
	return nil
}

func (db *mockDB) InsertClusterConfigInfo(row ClusterConfigInfoRow) (int64, error) {
	row.ID = int64(len(db.clusterConfigInfo))
	db.clusterConfigInfo = append(db.clusterConfigInfo, row)
	return row.ID, nil
}

func (db *mockDB) GetClusterConfigInfo(clusterType, clusterID, filename string) (ClusterConfigInfoRow, error) {
	for _, c := range db.clusterConfigInfo {
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.Filename == filename {
			return c, nil
		}
	}
	return ClusterConfigInfoRow{}, ErrNotFound
}

func (db *mockDB) ListClusterConfigInfo(clusterType, clusterID string) ([]ClusterConfigInfoRow, error) {
	var result []ClusterConfigInfoRow
	for _, c := range db.clusterConfigInfo {
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.DeleteTime == nil {
			result = append(result, c)
		}
	}
	return result, nil
}

func (db *mockDB) ListClusterConfigInfo2(clusterType, clusterID, configType, relatedComponent string) ([]ClusterConfigInfoRow, error) {
	var result []ClusterConfigInfoRow
	for _, c := range db.clusterConfigInfo {
		//
		if c.ClusterType == clusterType && c.DeleteTime == nil && (clusterID == "" || c.ClusterID == clusterID) && (configType == "" || c.ConfigType == configType) && (relatedComponent == "" || c.BelongTo == relatedComponent) {
			result = append(result, c)
		}
	}
	return result, nil
}

func (db *mockDB) CountClusterConfigInfoByTemplateID(templateID int64) (int64, error) {
	var count int64
	for _, row := range db.clusterConfigInfo {
		if row.TemplateID == templateID && row.DeleteTime == nil {
			count++
		}
	}
	return count, nil
}

func (db *mockDB) ListClusterConfigInfoByTemplate(clusterType, filename string, templateID int64) ([]ClusterConfigInfoRow, error) {
	var result []ClusterConfigInfoRow
	for _, row := range db.clusterConfigInfo {
		if row.ClusterType == clusterType && row.Filename == filename && row.TemplateID == templateID && row.DeleteTime == nil {
			result = append(result, row)
		}
	}
	return result, nil
}

func (db *mockDB) UpdateClusterConfigInfo(config ClusterConfigInfoRow) error {
	for _, row := range db.clusterConfigInfo {
		if row.ClusterType == config.ClusterType && row.ClusterID == config.ClusterID && row.Filename == config.Filename {
			row.TemplateID = config.TemplateID
			row.Format = config.Format
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) DeleteClusterConfigInfoByFile(clusterType, clusterID, filename string) error {
	now := time.Now()
	for i := range db.clusterConfigInfo {
		c := db.clusterConfigInfo[i]
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.Filename == filename && c.DeleteTime == nil {
			db.clusterConfigInfo[i].DeleteTime = &now
			return nil
		}
	}
	return nil
}

func (db *mockDB) DeleteClusterConfigInfoByCluster(clusterType, clusterID string) (int64, error) {
	now := time.Now()
	var count int64
	for i := range db.clusterConfigInfo {
		c := db.clusterConfigInfo[i]
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.DeleteTime == nil {
			db.clusterConfigInfo[i].DeleteTime = &now
			count++
		}
	}
	return count, nil
}

func (db *mockDB) InsertClusterConfigParams(clusterType, clusterID, filename string, items []ConfigurationItem) error {
	for _, item := range items {
		db.clusterConfigParams = append(db.clusterConfigParams, ClusterConfigParamRow{
			ClusterType:       clusterType,
			ClusterID:         clusterID,
			Filename:          filename,
			ConfigurationItem: item,
		})
	}
	return nil
}

func (db *mockDB) UpsertClusterConfigParams(clusterType, clusterID, filename string, items []ConfigurationItem) error {
	panic("not implemented")
}

func (db *mockDB) ListClusterConfigParams(clusterType, clusterID, filename string) ([]ClusterConfigParamRow, error) {
	var result []ClusterConfigParamRow
	for _, c := range db.clusterConfigParams {
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.Filename == filename {
			result = append(result, c)
		}
	}
	return result, nil
}

func (db *mockDB) DeleteClusterConfigParamsByFile(clusterType, clusterID, filename string) error {
	for i, c := range db.clusterConfigParams {
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.Filename == filename {
			db.clusterConfigParams = append(db.clusterConfigParams[:i], db.clusterConfigParams[i+1:]...)
			return nil
		}
	}
	return nil
}

func (db *mockDB) DeleteClusterConfigParamsByCluster(clusterType, clusterID string) error {
	count := 0
	for i, c := range db.clusterConfigParams {
		if c.ClusterType == clusterType && c.ClusterID == clusterID {
			db.clusterConfigParams = append(db.clusterConfigParams[:i], db.clusterConfigParams[i+1:]...)
			count++
		}
	}
	if count == 0 {
		return ErrNotFound
	}

	return nil
}

func (db *mockDB) CreateClusterConfigText(clusterType, clusterID, filename, text string) error {
	db.clusterConfigTexts = append(db.clusterConfigTexts, ClusterConfigTextRow{
		ClusterType: clusterType,
		ClusterID:   clusterID,
		Filename:    filename,
		Data:        text,
	})
	return nil
}

func (db *mockDB) GetClusterConfigText(clusterType, clusterID, filename string) (text string, err error) {
	for _, c := range db.clusterConfigTexts {
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.Filename == filename {
			return c.Data, nil
		}
	}
	return "", ErrNotFound
}

func (db *mockDB) UpdateClusterConfigText(clusterType, clusterID, filename, text string) error {
	for i, c := range db.clusterConfigTexts {
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.Filename == filename {
			db.clusterConfigTexts[i].Data = text
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) DeleteClusterConfigText(clusterType, clusterID, filename string) error {
	for i, c := range db.clusterConfigTexts {
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.Filename == filename {
			db.clusterConfigTexts = append(db.clusterConfigTexts[:i], db.clusterConfigTexts[i+1:]...)
			return nil
		}
	}
	return ErrNotFound
}

// GetConfigDict implements DB.
func (*mockDB) GetConfigDict(clusterType, config_type string, configKey string, version string) (*ConfigDictRow, error) {
	panic("unimplemented")
}

// InsertConfigDicts implements DB.
func (*mockDB) InsertConfigDicts(items []*ConfigDictRow) error {
	panic("unimplemented")
}

// ListConfigDict implements DB.
func (*mockDB) ListConfigDict(opt *ListConfigDictOption) ([]*ConfigDictRow, error) {
	panic("unimplemented")
}

// UpdateConfigDict implements DB.
func (*mockDB) UpdateConfigDict(item *ConfigDictRow) error {
	panic("unimplemented")
}

// GetTotalParamNum implements DB.
func (db *mockDB) GetTotalParamNum(opt *ListConfigDictOption) (int64, error) {
	panic("unimplemented")
}

// DeleteDictItems implements DB.
func (db *mockDB) DeleteDictItems(clusterType, configType string, version *string) (int64, error) {
	panic("unimplemented")
}

func (db *mockDB) InsertClusterComponent(cluster_type, cluster_id, component_name, version, cloud_provider, instance, overwrite_image_tag, base_image, image_tag string, update_strategy int) error {
	db.clusterComponents = append(db.clusterComponents, ClusterComponentRow{
		ClusterType:       cluster_type,
		ClusterID:         cluster_id,
		ComponentName:     component_name,
		Version:           version,
		CloudProvider:     cloud_provider,
		NodeInstance:      instance,
		OverwriteImageTag: overwrite_image_tag,
		BaseImage:         base_image,
		ImageTag:          image_tag,
		UpdateStrategy:    update_strategy,
	})
	return nil
}

func (db *mockDB) GetClusterComponent(clusterType, clusterID, componentName string) (ClusterComponentRow, error) {
	for _, c := range db.clusterComponents {
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.ComponentName == componentName {
			return c, nil
		}
	}
	return ClusterComponentRow{}, ErrNotFound
}

func (db *mockDB) ListClusterComponents(clusterType, clusterID string) ([]ClusterComponentRow, error) {
	components := []ClusterComponentRow{}
	for _, c := range db.clusterComponents {
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.DeleteTime == nil {
			components = append(components, c)
		}
	}
	return components, nil
}

func (db *mockDB) SearchClusterComponents(clusterType, componentName string, clusterIDs []string, componentVersion, imageTag string, showDeleted bool, pageSize, page int32) ([]ClusterComponentRow, error) {
	if pageSize <= 0 {
		pageSize = 100
	}
	if page <= 0 {
		page = 1
	}

	var components []ClusterComponentRow
	for _, c := range db.clusterComponents {
		if c.ClusterType == clusterType &&
			(showDeleted || c.DeleteTime == nil) &&
			(componentName == "" || c.ComponentName == componentName) &&
			(len(clusterIDs) == 0 || containsString(clusterIDs, c.ClusterID)) &&
			(componentVersion == "" || c.Version == componentVersion) &&
			(imageTag == "" || c.ImageTag == imageTag) {
			components = append(components, c)
		}
	}

	offset := int(int64(pageSize) * int64(page-1))
	if offset >= len(components) {
		return []ClusterComponentRow{}, nil
	}
	end := offset + int(pageSize)
	if end > len(components) {
		end = len(components)
	}
	return components[offset:end], nil
}

func (db *mockDB) CountClusterComponents(clusterType, componentName string, clusterIDs []string, componentVersion, imageTag string, showDeleted bool) (int64, error) {
	var total int64
	for _, c := range db.clusterComponents {
		if c.ClusterType == clusterType &&
			(showDeleted || c.DeleteTime == nil) &&
			(componentName == "" || c.ComponentName == componentName) &&
			(len(clusterIDs) == 0 || containsString(clusterIDs, c.ClusterID)) &&
			(componentVersion == "" || c.Version == componentVersion) &&
			(imageTag == "" || c.ImageTag == imageTag) {
			total++
		}
	}
	return total, nil
}

func containsString(items []string, target string) bool {
	for _, item := range items {
		if item == target {
			return true
		}
	}
	return false
}

func (db *mockDB) UpdateClusterComponent(cluster_type, cluster_id, component_name, version, cloud_provider, instance, overwrite_image_tag, base_image, image_tag string, update_strategy int) error {
	for i, c := range db.clusterComponents {
		if c.ClusterType == cluster_type && c.ClusterID == cluster_id && c.ComponentName == component_name {
			db.clusterComponents[i].Version = version
			db.clusterComponents[i].CloudProvider = cloud_provider
			db.clusterComponents[i].NodeInstance = instance
			db.clusterComponents[i].OverwriteImageTag = overwrite_image_tag
			db.clusterComponents[i].BaseImage = base_image
			db.clusterComponents[i].ImageTag = image_tag
			db.clusterComponents[i].UpdateStrategy = update_strategy
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) DeleteClusterComponentsByCluster(clusterType, clusterID string) (int64, error) {
	now := time.Now()
	var count int64
	for i := range db.clusterComponents {
		c := db.clusterComponents[i]
		if c.ClusterType == clusterType && c.ClusterID == clusterID && c.DeleteTime == nil {
			db.clusterComponents[i].DeleteTime = &now
			count++
		}
	}
	return count, nil
}

func (db *mockDB) InsertDefaultVersion(clusterType, component, clusterVersion, cloudProvider, region string, teanantID uint64, version string) error {
	for _, v := range db.defaultVersions {
		if v.ClusterType == clusterType && v.ComponentName == component && v.ClusterVersion == clusterVersion && v.CloudProvider == cloudProvider && v.Region == region && v.TenantID == teanantID {
			return ErrAlreadyExists
		}
	}

	db.defaultVersions = append(db.defaultVersions, DefaultVersionRow{
		ClusterType:   clusterType,
		ComponentName: component,
		CloudProvider: cloudProvider,
		Region:        region,
		TenantID:      teanantID,
		Version:       version,
	})
	return nil
}

func (db *mockDB) GetDefaultVersion(clusterType, component, clusterVersion, cloudProvider, region string, teanantID uint64) (string, error) {
	for _, v := range db.defaultVersions {
		if v.ClusterType == clusterType && v.ComponentName == component && v.ClusterVersion == clusterVersion && v.CloudProvider == cloudProvider && v.Region == region && v.TenantID == teanantID {
			return v.Version, nil
		}
	}
	return "", ErrNotFound
}

func (db *mockDB) ListDefaultVersionRules(clusterType string, component *string, tenantID *uint64) ([]DefaultVersionRow, error) {
	var result []DefaultVersionRow
	for _, v := range db.defaultVersions {
		if v.ClusterType == clusterType &&
			(component == nil || v.ComponentName == *component) &&
			(tenantID == nil || v.TenantID == *tenantID) {
			result = append(result, v)
		}
	}
	return result, nil
}

func (db *mockDB) UpdateDefaultVersion(clusterType, component, clusterVersion, cloudProvider, region string, teanantID uint64, version string) error {
	for i, v := range db.defaultVersions {
		if v.ClusterType == clusterType && v.ComponentName == component && v.ClusterVersion == clusterVersion && v.CloudProvider == cloudProvider && v.Region == region && v.TenantID == teanantID {
			db.defaultVersions[i].Version = version
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) DeleteDefaultVersion(clusterType, component, clusterVersion, cloudProvider, region string, teanantID uint64) error {
	for i, v := range db.defaultVersions {
		if v.ClusterType == clusterType && v.ComponentName == component && v.ClusterVersion == clusterVersion && v.CloudProvider == cloudProvider && v.Region == region && v.TenantID == teanantID {
			db.defaultVersions = append(db.defaultVersions[:i], db.defaultVersions[i+1:]...)
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) InsertComponentImage(clusterType, component, version, baseImage, tag string, tenantID uint64) error {
	for _, v := range db.componentImages {
		if v.ClusterType == clusterType && v.ComponentName == component && v.Version == version {
			return ErrAlreadyExists
		}
	}

	db.componentImages = append(db.componentImages, ComponentImageRow{
		ClusterType:   clusterType,
		ComponentName: component,
		Version:       version,
		BaseImage:     baseImage,
		Tag:           tag,
	})
	return nil
}

func (db *mockDB) GetComponentImage(clusterType, component, version string, tenantID uint64) (string, string, error) {
	for _, v := range db.componentImages {
		if v.ClusterType == clusterType && v.ComponentName == component && v.Version == version {
			return v.BaseImage, v.Tag, nil
		}
	}
	return "", "", ErrNotFound
}

func (db *mockDB) ListComponentImages(clusterType, component, version string) ([]ComponentImageRow, error) {
	var result []ComponentImageRow
	for _, v := range db.componentImages {
		if v.ClusterType == clusterType && v.ComponentName == component && (version == "" || v.Version == version) {
			result = append(result, v)
		}
	}
	return result, nil
}

func (db *mockDB) UpdateComponentImage(clusterType, component, version, baseImage, tag string, tenantID uint64) error {
	for i, v := range db.componentImages {
		if v.ClusterType == clusterType && v.ComponentName == component && v.Version == version {
			db.componentImages[i].BaseImage = baseImage
			db.componentImages[i].Tag = tag
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) UpsertComponentImage(clusterType, component, version, baseImage, tag string, tenantID uint64) error {
	for i, v := range db.componentImages {
		if v.ClusterType == clusterType && v.ComponentName == component && v.Version == version {
			db.componentImages[i].BaseImage = baseImage
			db.componentImages[i].Tag = tag
			return nil
		}
	}
	db.componentImages = append(db.componentImages, ComponentImageRow{
		ClusterType:   clusterType,
		ComponentName: component,
		Version:       version,
		BaseImage:     baseImage,
		Tag:           tag,
	})
	return nil
}

func (db *mockDB) DeleteComponentImage(clusterType, component, version string) error {
	for i, v := range db.componentImages {
		if v.ClusterType == clusterType && v.ComponentName == component && v.Version == version {
			db.componentImages = append(db.componentImages[:i], db.componentImages[i+1:]...)
			return nil
		}
	}
	return ErrNotFound
}

func (db *mockDB) InsertAuditLog(api_name, cluster_type, cluster_id string, request_ts_ms, commit_ts_ms int64, request, response string) error {
	db.auditLogs = append(db.auditLogs, AuditLogRow{
		ID:          int64(len(db.auditLogs)),
		APIName:     api_name,
		ClusterType: cluster_type,
		ClusterID:   cluster_id,
		RequestTsMs: request_ts_ms,
		CommitTsMs:  commit_ts_ms,
		Request:     request,
		Response:    response,
	})
	return nil
}

func (db *mockDB) ListVersionsFromComponentImage(clusterType, component string) ([]string, error) {
	versions := make(map[string]struct{})
	for _, v := range db.componentImages {
		if v.ClusterType == clusterType && v.ComponentName == component {
			versions[v.Version] = struct{}{}
		}
	}

	var result []string
	for version := range versions {
		result = append(result, version)
	}
	return result, nil
}

func (db *mockDB) ListVersionsFromConfigDict(string, string) ([]string, error) {
	panic("unimplemented")
}

func (db *mockDB) ListAvailableInstancesFromDefaultTemplate(clusterType string, configType, version *string) ([]UniqueInstance, error) {
	instances := make(map[UniqueInstance]struct{})
	for _, t := range db.defaultTemplates {
		if t.ClusterType == clusterType &&
			(configType == nil || t.ConfigType == *configType) &&
			(version == nil || t.Version == *version) {
			instances[UniqueInstance{t.CloudProvider, t.NodeInstance}] = struct{}{}
		}
	}

	var result []UniqueInstance
	for instance := range instances {
		result = append(result, instance)
	}
	return result, nil
}

func (db *mockDB) UpsertRegionalService(clusterType, logicalRegion, endpoint string) error {
	panic("not implemented")
}

func (db *mockDB) DeleteRegionalService(clusterType, logicalRegion string) error {
	panic("not implemented")
}

func (db *mockDB) ListRegionalServices() ([]*RegionRoute, error) {
	return nil, nil
}

func (db *mockDB) GetRegionalService(clusterType, logicalRegion string) (*RegionRoute, error) {
	return nil, nil
}

func (db *mockDB) UpdateRegionalServiceSyncStatus(clusterType, logicalRegion, syncStatus string) error {
	return nil
}
