/*
 * 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 unified provides shared data models and utility functions for
// compliance check results across all scanner engines. It defines status
// constants, severity levels, rule definitions, and summary computation logic.
package unified

import (
	ctrl "sigs.k8s.io/controller-runtime"

	compliancev1alpha1 "gitcode.com/openFuyao/compliance-operator/api/v1alpha1"
)

// modelLog is the package-level logger for unified model operations.
var modelLog = ctrl.Log.WithName("unified-model")

// Check status constants used across all scanner engines to normalize
// scanner-specific result values into a common vocabulary.
const (
	StatusPass          = "PASS"
	StatusFail          = "FAIL"
	StatusWarn          = "WARN"
	StatusInfo          = "INFO"
	StatusNotApplicable = "NOT_APPLICABLE"
	StatusNotChecked    = "NOT_CHECKED"
	StatusError         = "ERROR"
)

// Severity levels for check results, ordered from most to least critical.
const (
	SeverityHigh   = "high"
	SeverityMedium = "medium"
	SeverityLow    = "low"
	SeverityInfo   = "info"
)

// RuleDefinition represents a rule entry from the knowledge base.
// In production, these are embedded at compile time from YAML/XML files.
type RuleDefinition struct {
	// ID is the unique check identifier (e.g., "1.1.1" or "V-242376")
	ID string `json:"ruleID"`
	// Description is the human-readable check description
	Description string `json:"description"`
	// Remediation is the suggested fix for a failed check
	Remediation string `json:"remediation"`
	// Severity is the severity level (high, medium, low, info)
	Severity string `json:"severity"`
	// Category is the check category (e.g., "Control Plane", "Worker Node", "etcd")
	Category string `json:"category"`
	// Scored indicates whether this check contributes to the compliance score
	Scored bool `json:"scored"`
	// Benchmark is the standard this rule belongs to (e.g., "cis-1.8", "stig-v2r4")
	Benchmark string `json:"benchmark"`
}

// RenderedCheckResult combines the scan result with knowledge base information.
// Used only for report generation, not stored in etcd.
type RenderedCheckResult struct {
	compliancev1alpha1.UnifiedCheckResult
	RuleDefinition
}

// ComputeSummary calculates a ScanSummary from a list of UnifiedCheckResult.
// It iterates through all results and tallies counts by status category.
func ComputeSummary(results []compliancev1alpha1.UnifiedCheckResult) compliancev1alpha1.ScanSummary {
	modelLog.V(2).Info("computing summary", "resultCount", len(results))

	summary := compliancev1alpha1.ScanSummary{}
	for _, r := range results {
		summary.Total++
		tallyStatus(r.Status, &summary)
	}
	return summary
}

// statusTallyFunc increments a specific counter in the scan summary.
type statusTallyFunc func(*compliancev1alpha1.ScanSummary)

// statusTallyMap maps check status values to their corresponding counter increment functions.
var statusTallyMap = map[string]statusTallyFunc{
	StatusPass:          func(s *compliancev1alpha1.ScanSummary) { s.Pass++ },
	StatusFail:          func(s *compliancev1alpha1.ScanSummary) { s.Fail++ },
	StatusWarn:          func(s *compliancev1alpha1.ScanSummary) { s.Warn++ },
	StatusInfo:          func(s *compliancev1alpha1.ScanSummary) { s.Info++ },
	StatusNotApplicable: func(s *compliancev1alpha1.ScanSummary) { s.NotApplicable++ },
	StatusNotChecked:    func(s *compliancev1alpha1.ScanSummary) { s.NotChecked++ },
	StatusError:         func(s *compliancev1alpha1.ScanSummary) { s.Error++ },
}

// tallyStatus increments the appropriate counter in the summary based on the check status.
func tallyStatus(status string, summary *compliancev1alpha1.ScanSummary) {
	if fn, ok := statusTallyMap[status]; ok {
		fn(summary)
	}
}

// MergeSummaries aggregates multiple ScanSummary values into a single summary
// by summing all counter fields.
func MergeSummaries(summaries []compliancev1alpha1.ScanSummary) compliancev1alpha1.ScanSummary {
	merged := compliancev1alpha1.ScanSummary{}
	for _, s := range summaries {
		merged.Pass += s.Pass
		merged.Fail += s.Fail
		merged.Warn += s.Warn
		merged.Info += s.Info
		merged.NotApplicable += s.NotApplicable
		merged.NotChecked += s.NotChecked
		merged.Error += s.Error
		merged.Total += s.Total
	}
	return merged
}

// DetermineOverallResult returns the overall scan result based on the summary.
// Priority: Error (if Total==0 or Error>0) > NonCompliant (if Fail>0) > Compliant.
// A Total of 0 indicates no results were collected, which is treated as an error condition.
func DetermineOverallResult(summary compliancev1alpha1.ScanSummary) compliancev1alpha1.ScanResult {
	if summary.Total == 0 {
		modelLog.V(1).Info("no results collected, returning ResultError")
		return compliancev1alpha1.ResultError
	}
	if summary.Error > 0 {
		return compliancev1alpha1.ResultError
	}
	if summary.Fail > 0 {
		return compliancev1alpha1.ResultNonCompliant
	}
	if summary.Warn > 0 {
		return compliancev1alpha1.ResultNonCompliant
	}
	return compliancev1alpha1.ResultCompliant
}