package store

import (
	"encoding/json"
	"fmt"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/util"

	"github.com/grafana/grafana/pkg/services/ngalert/models"
)

func alertRuleToModelsAlertRule(ar alertRule, l log.Logger) (models.AlertRule, error) {
	var data []models.AlertQuery
	err := json.Unmarshal([]byte(ar.Data), &data)
	if err != nil {
		return models.AlertRule{}, fmt.Errorf("failed to parse data: %w", err)
	}

	result := models.AlertRule{
		ID:                          ar.ID,
		OrgID:                       ar.OrgID,
		GUID:                        ar.GUID,
		Title:                       ar.Title,
		Condition:                   ar.Condition,
		Data:                        data,
		Updated:                     ar.Updated,
		IntervalSeconds:             ar.IntervalSeconds,
		Version:                     ar.Version,
		UID:                         ar.UID,
		NamespaceUID:                ar.NamespaceUID,
		DashboardUID:                ar.DashboardUID,
		PanelID:                     ar.PanelID,
		RuleGroupIndex:              ar.RuleGroupIndex,
		For:                         ar.For,
		KeepFiringFor:               ar.KeepFiringFor,
		IsPaused:                    ar.IsPaused,
		MissingSeriesEvalsToResolve: ar.MissingSeriesEvalsToResolve,
	}

	if ar.RuleGroup == "" {
		noGroupRuleGroup, err := models.NewNoGroupRuleGroup(ar.UID)
		if err != nil {
			return models.AlertRule{}, fmt.Errorf("failed to create no group rule group: %w", err)
		}
		result.RuleGroup = noGroupRuleGroup.String()
	} else {
		result.RuleGroup = ar.RuleGroup
	}

	if ar.UpdatedBy != nil {
		result.UpdatedBy = util.Pointer(models.UserUID(*ar.UpdatedBy))
	}

	if ar.NoDataState != "" {
		result.NoDataState, err = models.NoDataStateFromString(ar.NoDataState)
		if err != nil {
			l.Warn("Unknown NoDataState value, defaulting to NoData", append(result.GetKey().LogContext(), "original", ar.NoDataState)...)
			result.NoDataState = models.NoData
		}
	}
	if ar.ExecErrState != "" {
		result.ExecErrState, err = models.ErrStateFromString(ar.ExecErrState)
		if err != nil {
			l.Warn("Unknown ExecErrState value, defaulting to Error", append(result.GetKey().LogContext(), "original", ar.ExecErrState)...)
			result.ExecErrState = models.ErrorErrState
		}
	}

	if ar.Record != "" {
		var record models.Record
		err = json.Unmarshal([]byte(ar.Record), &record)
		if err != nil {
			return models.AlertRule{}, fmt.Errorf("failed to parse record: %w", err)
		}
		result.Record = &record
	}

	if ar.Labels != "" {
		err = json.Unmarshal([]byte(ar.Labels), &result.Labels)
		if err != nil {
			return models.AlertRule{}, fmt.Errorf("failed to parse labels: %w", err)
		}
	}

	if ar.Annotations != "" {
		err = json.Unmarshal([]byte(ar.Annotations), &result.Annotations)
		if err != nil {
			return models.AlertRule{}, fmt.Errorf("failed to parse annotations: %w", err)
		}
	}

	if ar.NotificationSettings != "" {
		ns, err := parseNotificationSettings(ar.NotificationSettings)
		if err != nil {
			return models.AlertRule{}, fmt.Errorf("failed to parse notification settings: %w", err)
		}
		result.NotificationSettings = ns
	}

	if ar.Metadata != "" {
		err = json.Unmarshal([]byte(ar.Metadata), &result.Metadata)
		if err != nil {
			return models.AlertRule{}, fmt.Errorf("failed to metadata: %w", err)
		}
	}

	return result, nil
}

func parseNotificationSettings(s string) ([]models.NotificationSettings, error) {
	var result []models.NotificationSettings
	if err := json.Unmarshal([]byte(s), &result); err != nil {
		return nil, err
	}
	return result, nil
}

func alertRuleFromModelsAlertRule(ar models.AlertRule) (alertRule, error) {
	result := alertRule{
		ID:                          ar.ID,
		GUID:                        ar.GUID,
		OrgID:                       ar.OrgID,
		Title:                       ar.Title,
		Condition:                   ar.Condition,
		Updated:                     ar.Updated,
		IntervalSeconds:             ar.IntervalSeconds,
		Version:                     ar.Version,
		UID:                         ar.UID,
		NamespaceUID:                ar.NamespaceUID,
		DashboardUID:                ar.DashboardUID,
		PanelID:                     ar.PanelID,
		RuleGroupIndex:              ar.RuleGroupIndex,
		NoDataState:                 ar.NoDataState.String(),
		ExecErrState:                ar.ExecErrState.String(),
		For:                         ar.For,
		KeepFiringFor:               ar.KeepFiringFor,
		IsPaused:                    ar.IsPaused,
		MissingSeriesEvalsToResolve: ar.MissingSeriesEvalsToResolve,
	}

	if models.IsNoGroupRuleGroup(ar.RuleGroup) {
		result.RuleGroup = ""
	} else {
		result.RuleGroup = ar.RuleGroup
	}

	if ar.UpdatedBy != nil {
		result.UpdatedBy = util.Pointer(string(*ar.UpdatedBy))
	}

	// Serialize complex types to JSON strings
	data, err := json.Marshal(ar.Data)
	if err != nil {
		return alertRule{}, fmt.Errorf("failed to marshal data: %w", err)
	}
	result.Data = string(data)

	if ar.Record != nil {
		recordData, err := json.Marshal(ar.Record)
		if err != nil {
			return alertRule{}, fmt.Errorf("failed to marshal record: %w", err)
		}
		result.Record = string(recordData)
	}

	if len(ar.Annotations) > 0 {
		annotationsData, err := json.Marshal(ar.Annotations)
		if err != nil {
			return alertRule{}, fmt.Errorf("failed to marshal annotations: %w", err)
		}
		result.Annotations = string(annotationsData)
	}

	if len(ar.Labels) > 0 {
		labelsData, err := json.Marshal(ar.Labels)
		if err != nil {
			return alertRule{}, fmt.Errorf("failed to marshal labels: %w", err)
		}
		result.Labels = string(labelsData)
	}

	if len(ar.NotificationSettings) > 0 {
		notificationSettingsData, err := json.Marshal(ar.NotificationSettings)
		if err != nil {
			return alertRule{}, fmt.Errorf("failed to marshal notification settings: %w", err)
		}
		result.NotificationSettings = string(notificationSettingsData)
	}

	metadata, err := json.Marshal(ar.Metadata)
	if err != nil {
		return alertRule{}, fmt.Errorf("failed to metadata: %w", err)
	}
	result.Metadata = string(metadata)

	return result, nil
}

func alertRuleToAlertRuleVersion(rule alertRule) alertRuleVersion {
	return alertRuleVersion{
		RuleOrgID:                   rule.OrgID,
		RuleGUID:                    rule.GUID,
		RuleUID:                     rule.UID,
		RuleNamespaceUID:            rule.NamespaceUID,
		RuleGroup:                   rule.RuleGroup,
		RuleGroupIndex:              rule.RuleGroupIndex,
		ParentVersion:               0,
		RestoredFrom:                0,
		Version:                     rule.Version,
		Created:                     rule.Updated, // assuming the Updated time as the creation time
		CreatedBy:                   rule.UpdatedBy,
		Message:                     "", // Message is set by caller when creating versions
		Title:                       rule.Title,
		Condition:                   rule.Condition,
		Data:                        rule.Data,
		IntervalSeconds:             rule.IntervalSeconds,
		Record:                      rule.Record,
		NoDataState:                 rule.NoDataState,
		ExecErrState:                rule.ExecErrState,
		For:                         rule.For,
		KeepFiringFor:               rule.KeepFiringFor,
		Annotations:                 rule.Annotations,
		Labels:                      rule.Labels,
		IsPaused:                    rule.IsPaused,
		NotificationSettings:        rule.NotificationSettings,
		Metadata:                    rule.Metadata,
		MissingSeriesEvalsToResolve: rule.MissingSeriesEvalsToResolve,
	}
}

func alertRuleVersionToAlertRule(version alertRuleVersion) alertRule {
	return alertRule{
		ID:              version.ID,
		GUID:            version.RuleGUID,
		OrgID:           version.RuleOrgID,
		Title:           version.Title,
		Condition:       version.Condition,
		Data:            version.Data,
		Updated:         version.Created,
		UpdatedBy:       version.CreatedBy,
		IntervalSeconds: version.IntervalSeconds,
		Version:         version.Version,
		UID:             version.RuleUID,
		NamespaceUID:    version.RuleNamespaceUID,
		// Versions do not store Dashboard\Panel as separate column.
		// However, these fields are part of annotations and information in these fields is redundant
		DashboardUID:                nil,
		PanelID:                     nil,
		RuleGroup:                   version.RuleGroup,
		RuleGroupIndex:              version.RuleGroupIndex,
		Record:                      version.Record,
		NoDataState:                 version.NoDataState,
		ExecErrState:                version.ExecErrState,
		For:                         version.For,
		KeepFiringFor:               version.KeepFiringFor,
		Annotations:                 version.Annotations,
		Labels:                      version.Labels,
		IsPaused:                    version.IsPaused,
		NotificationSettings:        version.NotificationSettings,
		Metadata:                    version.Metadata,
		MissingSeriesEvalsToResolve: version.MissingSeriesEvalsToResolve,
	}
}

func alertRuleVersionToModelsAlertRuleVersion(version alertRuleVersion, l log.Logger) (models.AlertRuleVersion, error) {
	result, err := alertRuleToModelsAlertRule(alertRuleVersionToAlertRule(version), l)
	if err != nil {
		return models.AlertRuleVersion{}, err
	}

	return models.AlertRuleVersion{
		AlertRule: result,
		Message:   version.Message,
	}, nil
}
