// Package integration provides CI helper functions for BACKBEAT contract testing
package integration

import (
	"encoding/json"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
)

// CIHelper provides utilities for continuous integration testing
type CIHelper struct {
	validator *MessageValidator
}

// NewCIHelper creates a new CI helper with a message validator
func NewCIHelper(schemaDir string) (*CIHelper, error) {
	validator, err := NewMessageValidator(schemaDir)
	if err != nil {
		return nil, fmt.Errorf("failed to create validator: %w", err)
	}
	
	return &CIHelper{
		validator: validator,
	}, nil
}

// ValidateDirectory validates all JSON files in a directory against BACKBEAT schemas
func (ci *CIHelper) ValidateDirectory(dir string) (*DirectoryValidationResult, error) {
	result := &DirectoryValidationResult{
		Directory: dir,
		Files:     make(map[string]*FileValidationResult),
	}

	err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}

		// Skip non-JSON files
		if d.IsDir() || !strings.HasSuffix(strings.ToLower(path), ".json") {
			return nil
		}

		fileResult, validateErr := ci.validateFile(path)
		if validateErr != nil {
			result.Errors = append(result.Errors, fmt.Sprintf("Failed to validate %s: %v", path, validateErr))
		} else {
			relPath, _ := filepath.Rel(dir, path)
			result.Files[relPath] = fileResult
			result.TotalFiles++
			if fileResult.AllValid {
				result.ValidFiles++
			} else {
				result.InvalidFiles++
			}
		}

		return nil
	})

	if err != nil {
		return nil, fmt.Errorf("failed to walk directory: %w", err)
	}

	result.ValidationRate = float64(result.ValidFiles) / float64(result.TotalFiles)
	return result, nil
}

// validateFile validates a single JSON file
func (ci *CIHelper) validateFile(filePath string) (*FileValidationResult, error) {
	data, err := os.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read file: %w", err)
	}

	result := &FileValidationResult{
		FilePath: filePath,
		AllValid: true,
	}

	// Try to parse as single message first
	var singleMessage map[string]interface{}
	if err := json.Unmarshal(data, &singleMessage); err == nil {
		if msgType, hasType := singleMessage["type"].(string); hasType && ci.validator.IsMessageTypeSupported(msgType) {
			// Single BACKBEAT message
			validationResult, validateErr := ci.validator.ValidateMessage(data)
			if validateErr != nil {
				return nil, validateErr
			}
			result.Messages = []*ValidationResult{validationResult}
			result.AllValid = validationResult.Valid
			return result, nil
		}
	}

	// Try to parse as array of messages
	var messageArray []map[string]interface{}
	if err := json.Unmarshal(data, &messageArray); err == nil {
		for i, msg := range messageArray {
			msgBytes, marshalErr := json.Marshal(msg)
			if marshalErr != nil {
				result.Errors = append(result.Errors, fmt.Sprintf("Message %d: failed to marshal: %v", i, marshalErr))
				result.AllValid = false
				continue
			}

			validationResult, validateErr := ci.validator.ValidateMessage(msgBytes)
			if validateErr != nil {
				result.Errors = append(result.Errors, fmt.Sprintf("Message %d: validation error: %v", i, validateErr))
				result.AllValid = false
				continue
			}

			result.Messages = append(result.Messages, validationResult)
			if !validationResult.Valid {
				result.AllValid = false
			}
		}
		return result, nil
	}

	// Try to parse as examples format (array with description and message fields)
	var examples []ExampleMessage
	if err := json.Unmarshal(data, &examples); err == nil {
		for i, example := range examples {
			msgBytes, marshalErr := json.Marshal(example.Message)
			if marshalErr != nil {
				result.Errors = append(result.Errors, fmt.Sprintf("Example %d (%s): failed to marshal: %v", i, example.Description, marshalErr))
				result.AllValid = false
				continue
			}

			validationResult, validateErr := ci.validator.ValidateMessage(msgBytes)
			if validateErr != nil {
				result.Errors = append(result.Errors, fmt.Sprintf("Example %d (%s): validation error: %v", i, example.Description, validateErr))
				result.AllValid = false
				continue
			}

			result.Messages = append(result.Messages, validationResult)
			if !validationResult.Valid {
				result.AllValid = false
			}
		}
		return result, nil
	}

	return nil, fmt.Errorf("file does not contain valid JSON message format")
}

// ExampleMessage represents a message example with description
type ExampleMessage struct {
	Description string                 `json:"description"`
	Message     map[string]interface{} `json:"message"`
}

// DirectoryValidationResult contains results for validating a directory
type DirectoryValidationResult struct {
	Directory      string                            `json:"directory"`
	TotalFiles     int                               `json:"total_files"`
	ValidFiles     int                               `json:"valid_files"`
	InvalidFiles   int                               `json:"invalid_files"`
	ValidationRate float64                           `json:"validation_rate"`
	Files          map[string]*FileValidationResult  `json:"files"`
	Errors         []string                          `json:"errors,omitempty"`
}

// FileValidationResult contains results for validating a single file
type FileValidationResult struct {
	FilePath string               `json:"file_path"`
	AllValid bool                 `json:"all_valid"`
	Messages []*ValidationResult  `json:"messages"`
	Errors   []string             `json:"errors,omitempty"`
}

// GenerateCIReport generates a formatted report suitable for CI systems
func (ci *CIHelper) GenerateCIReport(result *DirectoryValidationResult) string {
	var sb strings.Builder
	
	sb.WriteString("BACKBEAT Contract Validation Report\n")
	sb.WriteString("===================================\n\n")
	
	sb.WriteString(fmt.Sprintf("Directory: %s\n", result.Directory))
	sb.WriteString(fmt.Sprintf("Total Files: %d\n", result.TotalFiles))
	sb.WriteString(fmt.Sprintf("Valid Files: %d\n", result.ValidFiles))
	sb.WriteString(fmt.Sprintf("Invalid Files: %d\n", result.InvalidFiles))
	sb.WriteString(fmt.Sprintf("Validation Rate: %.2f%%\n\n", result.ValidationRate*100))
	
	if len(result.Errors) > 0 {
		sb.WriteString("Directory-level Errors:\n")
		for _, err := range result.Errors {
			sb.WriteString(fmt.Sprintf("  - %s\n", err))
		}
		sb.WriteString("\n")
	}
	
	// Group files by validation status
	validFiles := make([]string, 0)
	invalidFiles := make([]string, 0)
	
	for filePath, fileResult := range result.Files {
		if fileResult.AllValid {
			validFiles = append(validFiles, filePath)
		} else {
			invalidFiles = append(invalidFiles, filePath)
		}
	}
	
	if len(validFiles) > 0 {
		sb.WriteString("Valid Files:\n")
		for _, file := range validFiles {
			sb.WriteString(fmt.Sprintf("  ✓ %s\n", file))
		}
		sb.WriteString("\n")
	}
	
	if len(invalidFiles) > 0 {
		sb.WriteString("Invalid Files:\n")
		for _, file := range invalidFiles {
			fileResult := result.Files[file]
			sb.WriteString(fmt.Sprintf("  ✗ %s\n", file))
			
			for _, err := range fileResult.Errors {
				sb.WriteString(fmt.Sprintf("    - %s\n", err))
			}
			
			for i, msg := range fileResult.Messages {
				if !msg.Valid {
					sb.WriteString(fmt.Sprintf("    Message %d (%s):\n", i+1, msg.MessageType))
					for _, valErr := range msg.Errors {
						sb.WriteString(fmt.Sprintf("      - %s: %s\n", valErr.Field, valErr.Message))
					}
				}
			}
			sb.WriteString("\n")
		}
	}
	
	return sb.String()
}

// ExitWithStatus exits the program with appropriate status code for CI
func (ci *CIHelper) ExitWithStatus(result *DirectoryValidationResult) {
	if result.InvalidFiles > 0 || len(result.Errors) > 0 {
		fmt.Fprint(os.Stderr, ci.GenerateCIReport(result))
		os.Exit(1)
	} else {
		fmt.Print(ci.GenerateCIReport(result))
		os.Exit(0)
	}
}

// ValidateExamples validates the built-in example messages
func (ci *CIHelper) ValidateExamples() ([]*ValidationResult, error) {
	examples := ExampleMessages()
	results := make([]*ValidationResult, 0, len(examples))
	
	for name, example := range examples {
		result, err := ci.validator.ValidateStruct(example)
		if err != nil {
			return nil, fmt.Errorf("failed to validate example %s: %w", name, err)
		}
		results = append(results, result)
	}
	
	return results, nil
}

// GetSchemaInfo returns information about loaded schemas
func (ci *CIHelper) GetSchemaInfo() map[string]string {
	info := make(map[string]string)
	for _, msgType := range ci.validator.GetSupportedMessageTypes() {
		info[msgType] = getSchemaVersion(msgType)
	}
	return info
}