// Package integration provides CI validation helpers for BACKBEAT conformance testing
package integration

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/xeipuuv/gojsonschema"
)

// MessageValidator provides validation for BACKBEAT messages against JSON schemas
type MessageValidator struct {
	schemaLoaders map[string]gojsonschema.JSONLoader
}

// MessageType constants for the three core BACKBEAT interfaces
const (
	BeatFrameType   = "backbeat.beatframe.v1"
	StatusClaimType = "backbeat.statusclaim.v1"
	BarReportType   = "backbeat.barreport.v1"
)

// ValidationError represents a validation failure with context
type ValidationError struct {
	MessageType string   `json:"message_type"`
	Field       string   `json:"field"`
	Value       string   `json:"value"`
	Message     string   `json:"message"`
	Errors      []string `json:"errors"`
}

func (ve ValidationError) Error() string {
	return fmt.Sprintf("validation failed for %s: %s", ve.MessageType, strings.Join(ve.Errors, "; "))
}

// ValidationResult contains the outcome of message validation
type ValidationResult struct {
	Valid        bool               `json:"valid"`
	MessageType  string             `json:"message_type"`
	Errors       []ValidationError  `json:"errors,omitempty"`
	SchemaVersion string            `json:"schema_version"`
}

// NewMessageValidator creates a new validator with schema loaders
func NewMessageValidator(schemaDir string) (*MessageValidator, error) {
	validator := &MessageValidator{
		schemaLoaders: make(map[string]gojsonschema.JSONLoader),
	}

	// Load all schema files
	schemas := map[string]string{
		BeatFrameType:   "beatframe-v1.schema.json",
		StatusClaimType: "statusclaim-v1.schema.json",
		BarReportType:   "barreport-v1.schema.json",
	}

	for msgType, schemaFile := range schemas {
		schemaPath := filepath.Join(schemaDir, schemaFile)
		loader := gojsonschema.NewReferenceLoader("file://" + schemaPath)
		validator.schemaLoaders[msgType] = loader
	}

	return validator, nil
}

// ValidateMessage validates a JSON message against the appropriate BACKBEAT schema
func (v *MessageValidator) ValidateMessage(messageJSON []byte) (*ValidationResult, error) {
	// Parse message to determine type
	var msgMap map[string]interface{}
	if err := json.Unmarshal(messageJSON, &msgMap); err != nil {
		return nil, fmt.Errorf("failed to parse JSON: %w", err)
	}

	msgType, ok := msgMap["type"].(string)
	if !ok {
		return &ValidationResult{
			Valid:       false,
			MessageType: "unknown",
			Errors: []ValidationError{
				{
					Field:   "type",
					Message: "message type field is missing or not a string",
					Errors:  []string{"type field is required and must be a string"},
				},
			},
		}, nil
	}

	// Get appropriate schema loader
	schemaLoader, exists := v.schemaLoaders[msgType]
	if !exists {
		return &ValidationResult{
			Valid:       false,
			MessageType: msgType,
			Errors: []ValidationError{
				{
					Field:   "type",
					Value:   msgType,
					Message: fmt.Sprintf("unsupported message type: %s", msgType),
					Errors:  []string{fmt.Sprintf("message type %s is not supported by BACKBEAT contracts", msgType)},
				},
			},
		}, nil
	}

	// Validate against schema
	docLoader := gojsonschema.NewBytesLoader(messageJSON)
	result, err := gojsonschema.Validate(schemaLoader, docLoader)
	if err != nil {
		return nil, fmt.Errorf("schema validation failed: %w", err)
	}

	validationResult := &ValidationResult{
		Valid:         result.Valid(),
		MessageType:   msgType,
		SchemaVersion: getSchemaVersion(msgType),
	}

	if !result.Valid() {
		for _, desc := range result.Errors() {
			validationResult.Errors = append(validationResult.Errors, ValidationError{
				MessageType: msgType,
				Field:       desc.Field(),
				Value:       fmt.Sprintf("%v", desc.Value()),
				Message:     desc.Description(),
				Errors:      []string{desc.String()},
			})
		}
	}

	return validationResult, nil
}

// ValidateMessageString validates a JSON message string
func (v *MessageValidator) ValidateMessageString(messageJSON string) (*ValidationResult, error) {
	return v.ValidateMessage([]byte(messageJSON))
}

// ValidateStruct validates a Go struct by marshaling to JSON first
func (v *MessageValidator) ValidateStruct(message interface{}) (*ValidationResult, error) {
	jsonBytes, err := json.Marshal(message)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal struct to JSON: %w", err)
	}
	return v.ValidateMessage(jsonBytes)
}

// BatchValidate validates multiple messages and returns aggregated results
func (v *MessageValidator) BatchValidate(messages [][]byte) ([]*ValidationResult, error) {
	results := make([]*ValidationResult, len(messages))
	for i, msg := range messages {
		result, err := v.ValidateMessage(msg)
		if err != nil {
			return nil, fmt.Errorf("failed to validate message %d: %w", i, err)
		}
		results[i] = result
	}
	return results, nil
}

// GetSupportedMessageTypes returns the list of supported BACKBEAT message types
func (v *MessageValidator) GetSupportedMessageTypes() []string {
	types := make([]string, 0, len(v.schemaLoaders))
	for msgType := range v.schemaLoaders {
		types = append(types, msgType)
	}
	return types
}

// IsMessageTypeSupported checks if a message type is supported
func (v *MessageValidator) IsMessageTypeSupported(msgType string) bool {
	_, exists := v.schemaLoaders[msgType]
	return exists
}

// getSchemaVersion returns the version for a given message type
func getSchemaVersion(msgType string) string {
	versions := map[string]string{
		BeatFrameType:   "1.0.0",
		StatusClaimType: "1.0.0", 
		BarReportType:   "1.0.0",
	}
	return versions[msgType]
}

// ValidationStats provides summary statistics for batch validation
type ValidationStats struct {
	TotalMessages    int                    `json:"total_messages"`
	ValidMessages    int                    `json:"valid_messages"`
	InvalidMessages  int                    `json:"invalid_messages"`
	MessageTypes     map[string]int         `json:"message_types"`
	ErrorSummary     map[string]int         `json:"error_summary"`
	ValidationRate   float64                `json:"validation_rate"`
}

// GetValidationStats computes statistics from validation results
func GetValidationStats(results []*ValidationResult) *ValidationStats {
	stats := &ValidationStats{
		TotalMessages: len(results),
		MessageTypes:  make(map[string]int),
		ErrorSummary:  make(map[string]int),
	}

	for _, result := range results {
		// Count message types
		stats.MessageTypes[result.MessageType]++

		if result.Valid {
			stats.ValidMessages++
		} else {
			stats.InvalidMessages++
			// Aggregate error types
			for _, err := range result.Errors {
				stats.ErrorSummary[err.Field]++
			}
		}
	}

	if stats.TotalMessages > 0 {
		stats.ValidationRate = float64(stats.ValidMessages) / float64(stats.TotalMessages)
	}

	return stats
}

// ExampleMessages provides sample messages for testing and documentation
func ExampleMessages() map[string]interface{} {
	return map[string]interface{}{
		"beatframe_minimal": map[string]interface{}{
			"type":        BeatFrameType,
			"cluster_id":  "test-cluster",
			"beat_index":  0,
			"downbeat":    true,
			"phase":       "plan",
			"hlc":         "0001:0000:cafe",
			"deadline_at": "2025-09-05T12:00:30Z",
			"tempo_bpm":   2.0,
			"window_id":   "a1b2c3d4e5f6789012345678901234ab",
		},
		"statusclaim_minimal": map[string]interface{}{
			"type":       StatusClaimType,
			"agent_id":   "test:agent",
			"beat_index": 100,
			"state":      "idle",
			"hlc":        "7ffd:0001:abcd",
		},
		"barreport_minimal": map[string]interface{}{
			"type":                    BarReportType,
			"window_id":               "7e9b0e6c4c9a4e59b7f2d9a3c1b2e4d5",
			"from_beat":               0,
			"to_beat":                 119,
			"agents_reporting":        1,
			"on_time_reviews":         1,
			"help_promises_fulfilled": 0,
			"secret_rotations_ok":     true,
			"tempo_drift_ms":          0.0,
		},
	}
}

// PrettyPrintValidationResult formats validation results for human reading
func PrettyPrintValidationResult(result *ValidationResult) string {
	var sb strings.Builder
	
	sb.WriteString(fmt.Sprintf("Message Type: %s\n", result.MessageType))
	sb.WriteString(fmt.Sprintf("Schema Version: %s\n", result.SchemaVersion))
	sb.WriteString(fmt.Sprintf("Valid: %t\n", result.Valid))
	
	if !result.Valid && len(result.Errors) > 0 {
		sb.WriteString("\nValidation Errors:\n")
		for i, err := range result.Errors {
			sb.WriteString(fmt.Sprintf("  %d. Field: %s\n", i+1, err.Field))
			if err.Value != "" {
				sb.WriteString(fmt.Sprintf("     Value: %s\n", err.Value))
			}
			sb.WriteString(fmt.Sprintf("     Error: %s\n", err.Message))
		}
	}
	
	return sb.String()
}