package message

import (
	"encoding/json"
	"time"

	helmv2 "github.com/fluxcd/helm-controller/api/v2"

	"github.com/tidbcloud/observability/pkg/common/constants"
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// HelmRelease status constants
const (
	HelmReleaseStatusReady       = "Ready"
	HelmReleaseStatusReleased    = "Released"
	HelmReleaseStatusTestSuccess = "TestSuccess"
	HelmReleaseStatusRemediated  = "Remediated"
)

// HelmRelease annotation constants
const (
	HelmReleaseExpectedDigestAnnotation = "observability.pingcap.com/expected-chart-digest"
	HelmReleaseResolvedDigestAnnotation = "observability.pingcap.com/resolved-chart-digest"
)

var (
	HelmRepoRef = &helmv2.CrossNamespaceObjectReference{
		Kind:      "HelmRepository",
		Name:      "o11y-helm-repo",
		Namespace: constants.OpsSystemNamespace,
	}
	DefaultInterval = metav1.Duration{Duration: time.Hour * 1}
)

// HelmReleaseSourceRef represents a reference to a Helm repository or other source
type HelmReleaseSourceRef struct {
	Kind      string `json:"kind,omitempty"`
	Name      string `json:"name,omitempty"`
	Namespace string `json:"namespace,omitempty"`
	URL       string `json:"url,omitempty"`
}

// HelmReleaseConfig represents simplified configuration for Helm releases
type HelmReleaseConfig struct {
	Values         map[string]interface{} `json:"values,omitempty"`                        // Chart values override
	Timeout        string                 `json:"timeout,omitempty"`                       // Timeout for operations (e.g., "5m")
	Interval       *metav1.Duration       `json:"interval,omitempty" swaggertype:"string"` // Interval for reconciliation (e.g., "5m")
	DependsOn      []string               `json:"dependsOn,omitempty"`                    // Names of HelmReleases this depends on
	Suspend        bool                   `json:"suspend,omitempty"`                       // Whether to suspend reconciliation
	Version        string                 `json:"version,omitempty"`                       // Version of the chart to install
	SourceRef      *HelmReleaseSourceRef  `json:"sourceRef,omitempty"`                     // Reference to Helm repository
	ExpectedDigest string                 `json:"expectedDigest,omitempty"`               // Expected chart digest
}

// CreateHelmReleaseReq represents the simplified request to create a new HelmRelease
type CreateHelmReleaseReq struct {
	// Name is the name of the HelmRelease, it will be used as the release name
	Name string `json:"name" validate:"required,max=64"`
	// Namespace is the namespace of the HelmRelease, it will be used as the target namespace
	Namespace string            `json:"namespace" validate:"required,max=64"`
	Component string            `json:"component" validate:"required"` // Pre-defined component name (e.g., "loki-ha", "kube-prometheus-stack")
	Config    HelmReleaseConfig `json:"config,omitempty"`
}

// CreateHelmReleaseResp represents the response after creating a HelmRelease
type CreateHelmReleaseResp struct {
	Name       string                   `json:"name"`
	Namespace  string                   `json:"namespace"`
	Component  string                   `json:"component"`
	Config     HelmReleaseConfig        `json:"config"`
	Status     HelmReleaseStatusSummary `json:"status"`
	CreateTime time.Time                `json:"createTime"`
}

// GetHelmReleaseReq represents the request to get a specific HelmRelease
type GetHelmReleaseReq struct {
	Name string `json:"name" validate:"required"`
}

// GetHelmReleaseResp represents the response with HelmRelease details
type GetHelmReleaseResp struct {
	Name       string                   `json:"name"`
	Namespace  string                   `json:"namespace"`
	Component  string                   `json:"component"`
	Config     HelmReleaseConfig        `json:"config"`
	Status     HelmReleaseStatusSummary `json:"status"`
	CreateTime time.Time                `json:"createTime"`
	UpdateTime time.Time                `json:"updateTime"`
} 

// ListHelmReleasesReq represents the request to list HelmReleases
type ListHelmReleasesReq struct {
	ContinueRequest
}

// ListHelmReleasesResp represents the response with a list of HelmReleases
type ListHelmReleasesResp struct {
	HelmReleases []HelmReleaseSummary `json:"helmReleases"`
	ContinueResponse
}

// HelmReleaseSummary represents a summary of HelmRelease info for listing
type HelmReleaseSummary struct {
	Name       string                   `json:"name"`
	Namespace  string                   `json:"namespace"`
	Component  string                   `json:"component"`
	Config     HelmReleaseConfigSummary `json:"config"`
	Status     HelmReleaseStatusSummary `json:"status"`
	CreateTime time.Time                `json:"createTime"`
	UpdateTime time.Time                `json:"updateTime"`
}

// HelmReleaseConfigSummary contains summary information from HelmReleaseConfig
type HelmReleaseConfigSummary struct {
	TargetNamespace string   `json:"targetNamespace,omitempty"`
	Suspend         bool     `json:"suspend,omitempty"`
	DependsOn       []string `json:"dependsOn,omitempty"`
}

// HelmReleaseStatusSummary contains summary information from HelmReleaseStatus
type HelmReleaseStatusSummary struct {
	ObservedGeneration    int64              `json:"observedGeneration,omitempty"`
	Conditions            []metav1.Condition `json:"conditions,omitempty" swaggerignore:"true"`
	LastAppliedRevision   string             `json:"lastAppliedRevision,omitempty"`
	LastAttemptedRevision string             `json:"lastAttemptedRevision,omitempty"`
	LastReleaseRevision   int                `json:"lastReleaseRevision,omitempty"`
	HelmChart             string             `json:"helmChart,omitempty"`
	Failures              int64              `json:"failures,omitempty"`
	LastFailureReason     string             `json:"lastFailureReason,omitempty"`
	RequestedVersion      string             `json:"requestedVersion,omitempty"`
	ResolvedDigest        string             `json:"resolvedDigest,omitempty"`
	ExpectedDigest        string             `json:"expectedDigest,omitempty"`
}

// UpdateHelmReleaseReq represents the simplified request to update an existing HelmRelease
type UpdateHelmReleaseReq struct {
	Name      string            `json:"name" validate:"required"`
	Component string            `json:"component" validate:"required"` // Pre-defined component name
	Config    HelmReleaseConfig `json:"config,omitempty"`
}

// UpdateHelmReleaseResp represents the response after updating a HelmRelease
type UpdateHelmReleaseResp struct {
	Name       string                   `json:"name"`
	Namespace  string                   `json:"namespace"`
	Component  string                   `json:"component"`
	Config     HelmReleaseConfig        `json:"config"`
	Status     HelmReleaseStatusSummary `json:"status"`
	UpdateTime time.Time                `json:"updateTime"`
}

// DeleteHelmReleaseReq represents the request to delete a HelmRelease
type DeleteHelmReleaseReq struct {
	Name string `json:"name" validate:"required"`
}

// DeleteHelmReleaseResp represents the response after deleting a HelmRelease
type DeleteHelmReleaseResp struct {
	Message string `json:"message"`
}

// UpdateBasicAuthReq represents the request to update basic auth credentials
type UpdateBasicAuthReq struct {
	Username string `json:"username" validate:"required"`
	Password string `json:"password" validate:"required"`
}

// UpdateBasicAuthResp represents the response after updating basic auth credentials
type UpdateBasicAuthResp struct {
}

// ConvertToFluxCDHelmRelease converts simplified API request to FluxCD HelmRelease CR
func ConvertToFluxCDHelmRelease(name, namespace, component string, config HelmReleaseConfig) (*helmv2.HelmRelease, error) {
	interval := DefaultInterval
	if config.Interval != nil {
		interval = *config.Interval
	}

	// Use default HelmRepoRef unless custom SourceRef is provided
	sourceRef := *HelmRepoRef
	if config.SourceRef != nil {
		sourceRef = helmv2.CrossNamespaceObjectReference{
			Kind:      config.SourceRef.Kind,
			Name:      config.SourceRef.Name,
			Namespace: config.SourceRef.Namespace,
		}
	}

	// Build annotations
	annotations := map[string]string{}
	if config.ExpectedDigest != "" {
		annotations[HelmReleaseExpectedDigestAnnotation] = config.ExpectedDigest
	}

	helmRelease := &helmv2.HelmRelease{
		ObjectMeta: metav1.ObjectMeta{
			Name:        name,
			Namespace:   constants.OpsSystemNamespace,
			Annotations: annotations,
			Labels: map[string]string{
				constants.LabelComponent:       component,
				"app.kubernetes.io/managed-by": "o11y-ops-server",
			},
		},
		Spec: helmv2.HelmReleaseSpec{
			Interval:        interval,
			ReleaseName:     name, // Use name as release name
			TargetNamespace: namespace,
			Suspend:         config.Suspend,
			Chart: &helmv2.HelmChartTemplate{
				Spec: helmv2.HelmChartTemplateSpec{
					Chart:     component,
					SourceRef: sourceRef,
					Version:   config.Version,
				},
			},
		},
	}
	// Convert values to JSON if provided
	if len(config.Values) > 0 {
		valuesJSON, err := ConvertValuesToJSON(config.Values)
		if err != nil {
			return nil, err
		}
		helmRelease.Spec.Values = valuesJSON
	}

	// Configure timeout
	if config.Timeout != "" {
		if duration, err := time.ParseDuration(config.Timeout); err == nil {
			helmRelease.Spec.Timeout = &metav1.Duration{Duration: duration}
		}
	}

	// Configure install settings
	helmRelease.Spec.Install = &helmv2.Install{
		CreateNamespace: true,
		CRDs:            helmv2.Create, // Default to creating CRDs
		Remediation: &helmv2.InstallRemediation{
			Retries: 3,
		},
	}

	// Configure upgrade settings
	helmRelease.Spec.Upgrade = &helmv2.Upgrade{
		CRDs: helmv2.CreateReplace, // Default to creating/replacing CRDs on upgrade
		Remediation: &helmv2.UpgradeRemediation{
			Retries: 3,
		},
	}

	return helmRelease, nil
}

// convertValuesToJSON converts a map of values to apiextensionsv1.JSON
func ConvertValuesToJSON(values map[string]interface{}) (*apiextensionsv1.JSON, error) {
	valuesJSON := &apiextensionsv1.JSON{}
	raw, err := json.Marshal(values)
	if err != nil {
		return nil, err
	}
	valuesJSON.Raw = raw
	return valuesJSON, nil
}

// ConvertFromFluxCDHelmRelease converts FluxCD HelmRelease CR to simplified API response
func ConvertFromFluxCDHelmRelease(hr *helmv2.HelmRelease) (string, HelmReleaseConfig) {
	// Extract component from labels
	component := ""
	if hr.Labels != nil {
		component = hr.Labels[constants.LabelComponent]
	}

	config := HelmReleaseConfig{
		Suspend: hr.Spec.Suspend,
	}

	// Extract version
	if hr.Spec.Chart != nil {
		config.Version = hr.Spec.Chart.Spec.Version

		// Extract source reference.
		// SourceRef.URL is not stored in the Flux HelmRelease CR; callers
		// that need URL for diff must supply it explicitly in the request.
		sourceRef := hr.Spec.Chart.Spec.SourceRef
		config.SourceRef = &HelmReleaseSourceRef{
			Kind:      sourceRef.Kind,
			Name:      sourceRef.Name,
			Namespace: sourceRef.Namespace,
		}
	}

	// Extract digest annotations
	if hr.Annotations != nil {
		if expectedDigest, ok := hr.Annotations[HelmReleaseExpectedDigestAnnotation]; ok {
			config.ExpectedDigest = expectedDigest
		}
	}

	// Extract values
	if hr.Spec.Values != nil && len(hr.Spec.Values.Raw) > 0 {
		var values map[string]interface{}
		if err := json.Unmarshal(hr.Spec.Values.Raw, &values); err == nil {
			config.Values = values
		}
	}

	// Extract timeout
	if hr.Spec.Timeout != nil {
		config.Timeout = hr.Spec.Timeout.Duration.String()
	}

	return component, config
}

// ConvertFluxCDStatusToSummary converts FluxCD HelmReleaseStatus to summary
func ConvertFluxCDStatusToSummary(status helmv2.HelmReleaseStatus) HelmReleaseStatusSummary {
	summary := HelmReleaseStatusSummary{
		ObservedGeneration:    status.ObservedGeneration,
		Conditions:            status.Conditions,
		LastAttemptedRevision: status.LastAttemptedRevision,
		LastReleaseRevision:   status.LastReleaseRevision,
		HelmChart:             status.HelmChart,
		Failures:              status.Failures,
	}

	// Derive LastAppliedRevision from the most recent successful snapshot in History
	if len(status.History) > 0 {
		// History is ordered with most recent first
		summary.LastAppliedRevision = status.History[0].ChartVersion
	}

	// Extract failure reason from first non-ready condition
	for _, condition := range status.Conditions {
		if condition.Type == "Ready" && condition.Status == metav1.ConditionFalse {
			summary.LastFailureReason = condition.Reason
			break
		}
	}

	return summary
}

type DiffHelmReleaseReq struct {
	Name         string            `json:"name" validate:"required,max=64"`
	Namespace    string            `json:"namespace" validate:"required,max=64"`
	Component    string            `json:"component" validate:"required"`
	Config       HelmReleaseConfig `json:"config,omitempty"`
	AdoptionMode bool              `json:"adoptionMode,omitempty"`
}

type HelmReleaseRenderedDiff struct {
	Changed        bool   `json:"changed"`
	Output         string `json:"output,omitempty"`
	ResolvedDigest string `json:"resolvedDigest,omitempty"`
}

type HelmReleaseSourceResolution struct {
	SourceKind      string `json:"sourceKind,omitempty"`
	SourceName      string `json:"sourceName,omitempty"`
	SourceNamespace string `json:"sourceNamespace,omitempty"`
	Chart           string `json:"chart,omitempty"`
	Version         string `json:"version,omitempty"`
	ExpectedDigest  string `json:"expectedDigest,omitempty"`
	ResolvedDigest  string `json:"resolvedDigest,omitempty"`
}

type DiffHelmReleaseResp struct {
	Name             string                      `json:"name"`
	Namespace        string                      `json:"namespace"`
	Component        string                      `json:"component"`
	RenderedDiff     HelmReleaseRenderedDiff     `json:"renderedDiff"`
	SourceResolution HelmReleaseSourceResolution `json:"sourceResolution"`
	SafeToApply      bool                        `json:"safeToApply"`
	Warnings         []string                    `json:"warnings,omitempty"`
	BlockingReasons  []string                    `json:"blockingReasons,omitempty"`
}
