/*
 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
 * openFuyao is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package collector

import (
	"encoding/json"
	"fmt"

	"many-core-orchestrator/internal/collector/collectors"
	"many-core-orchestrator/pkg/api/analyzerv1"
)

func snapshotToProto(snapshot Snapshot) *analyzerv1.MetricsSnapshot {
	return &analyzerv1.MetricsSnapshot{
		Node:      snapshot.Node,
		Timestamp: snapshot.Timestamp.Format("2006-01-02T15:04:05.999999999Z07:00"),
		Metrics: &analyzerv1.NodeMetrics{
			LlcMissRate:       snapshot.Metrics.LLCMissRate,
			LlcOccupancy:      snapshot.Metrics.LLCOccupancy,
			BioLatencyP95Ms:   snapshot.Metrics.BioLatencyP95Ms,
			BioLatencyP99Ms:   snapshot.Metrics.BioLatencyP99Ms,
			PsiAvailable:      snapshot.Metrics.PSIAvailable,
			PsiIoSome:         snapshot.Metrics.PSIIOAvg10,
			PsiIoFull:         snapshot.Metrics.PSIIOFullAvg10,
			PsiMemorySome:     snapshot.Metrics.PSIMemAvg10,
			PsiMemoryFull:     snapshot.Metrics.PSIMemFullAvg10,
			MemAvailableRatio: snapshot.Metrics.MemAvailRatio,
			PageScanRate:      snapshot.Metrics.PageScanRate,
		},
		Status:       snapshot.Status,
		Collectors:   collectorResultsToProto(snapshot.Collectors),
		SamplePeriod: snapshot.SamplePeriod,
		Sequence:     snapshot.Sequence,
		Availability: metricAvailabilityToProto(snapshot.Availability),
	}
}

func collectorResultsToProto(results []collectors.MetricResult) []*analyzerv1.CollectorResult {
	converted := make([]*analyzerv1.CollectorResult, 0, len(results))
	for _, result := range results {
		converted = append(converted, &analyzerv1.CollectorResult{
			Name:    result.Name,
			Values:  result.Values,
			Status:  result.Status,
			Error:   result.Error,
			Details: stringifyDetails(result.Details),
		})
	}
	return converted
}

func metricAvailabilityToProto(availability []MetricAvailability) []*analyzerv1.MetricAvailability {
	converted := make([]*analyzerv1.MetricAvailability, 0, len(availability))
	for _, item := range availability {
		converted = append(converted, &analyzerv1.MetricAvailability{
			Collector: item.Collector,
			Metric:    item.Metric,
			Available: item.Available,
			Status:    item.Status,
			Reason:    item.Reason,
		})
	}
	return converted
}

func stringifyDetails(details map[string]interface{}) map[string]string {
	if len(details) == 0 {
		return nil
	}
	converted := make(map[string]string, len(details))
	for key, value := range details {
		switch typed := value.(type) {
		case string:
			converted[key] = typed
		default:
			data, err := json.Marshal(typed)
			if err != nil {
				converted[key] = fmt.Sprint(typed)
				continue
			}
			converted[key] = string(data)
		}
	}
	return converted
}