package validation

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"strings"

	"github.com/go-chi/render"
	"github.com/rs/zerolog/log"
)

// Common validation patterns
var (
	// AlphaNumeric allows letters, numbers, hyphens and underscores
	AlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
	
	// ProjectName allows alphanumeric, spaces, hyphens, underscores (max 100 chars)
	ProjectName = regexp.MustCompile(`^[a-zA-Z0-9\s_-]{1,100}$`)
	
	// GitURL validates basic git URL structure
	GitURL = regexp.MustCompile(`^https?:\/\/[a-zA-Z0-9.-]+\/[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+(?:\.git)?$`)
	
	// TaskTitle allows reasonable task title characters (max 200 chars)
	TaskTitle = regexp.MustCompile(`^[a-zA-Z0-9\s.,!?()_-]{1,200}$`)
	
	// AgentID should be alphanumeric with hyphens (max 50 chars)
	AgentID = regexp.MustCompile(`^[a-zA-Z0-9-]{1,50}$`)
	
	// UUID pattern for council IDs, task IDs, etc.
	UUID = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
)

// ValidationError represents a validation error
type ValidationError struct {
	Field   string `json:"field"`
	Message string `json:"message"`
}

// ValidationErrors is a slice of validation errors
type ValidationErrors []ValidationError

func (v ValidationErrors) Error() string {
	if len(v) == 0 {
		return ""
	}
	if len(v) == 1 {
		return fmt.Sprintf("%s: %s", v[0].Field, v[0].Message)
	}
	return fmt.Sprintf("validation failed for %d fields", len(v))
}

// Validator provides request validation utilities
type Validator struct {
	maxBodySize int64
}

// NewValidator creates a new validator with default settings
func NewValidator() *Validator {
	return &Validator{
		maxBodySize: 1024 * 1024, // 1MB default
	}
}

// WithMaxBodySize sets the maximum request body size
func (v *Validator) WithMaxBodySize(size int64) *Validator {
	v.maxBodySize = size
	return v
}

// DecodeAndValidateJSON safely decodes JSON with size limits and validation
func (v *Validator) DecodeAndValidateJSON(r *http.Request, dest interface{}) error {
	// Limit request body size to prevent DoS attacks
	r.Body = http.MaxBytesReader(nil, r.Body, v.maxBodySize)
	
	// Decode JSON
	if err := json.NewDecoder(r.Body).Decode(dest); err != nil {
		log.Warn().Err(err).Msg("JSON decode error")
		return fmt.Errorf("invalid JSON: %w", err)
	}
	
	return nil
}

// ValidateProjectRequest validates project creation/update requests
func ValidateProjectRequest(req map[string]interface{}) ValidationErrors {
	var errors ValidationErrors
	
	// Validate name
	name, ok := req["name"].(string)
	if !ok || name == "" {
		errors = append(errors, ValidationError{
			Field:   "name",
			Message: "name is required",
		})
	} else if !ProjectName.MatchString(name) {
		errors = append(errors, ValidationError{
			Field:   "name",
			Message: "name contains invalid characters or is too long (max 100 chars)",
		})
	}
	
	// Validate repo_url
	repoURL, ok := req["repo_url"].(string)
	if !ok || repoURL == "" {
		errors = append(errors, ValidationError{
			Field:   "repo_url",
			Message: "repo_url is required",
		})
	} else {
		if !GitURL.MatchString(repoURL) {
			errors = append(errors, ValidationError{
				Field:   "repo_url",
				Message: "invalid git repository URL format",
			})
		} else {
			// Additional URL validation
			if _, err := url.Parse(repoURL); err != nil {
				errors = append(errors, ValidationError{
					Field:   "repo_url",
					Message: "malformed URL",
				})
			}
		}
	}
	
	// Validate optional description
	if desc, exists := req["description"]; exists {
		if descStr, ok := desc.(string); ok && len(descStr) > 1000 {
			errors = append(errors, ValidationError{
				Field:   "description",
				Message: "description too long (max 1000 chars)",
			})
		}
	}
	
	return errors
}

// ValidateTaskRequest validates task creation/update requests
func ValidateTaskRequest(req map[string]interface{}) ValidationErrors {
	var errors ValidationErrors
	
	// Validate title
	title, ok := req["title"].(string)
	if !ok || title == "" {
		errors = append(errors, ValidationError{
			Field:   "title",
			Message: "title is required",
		})
	} else if !TaskTitle.MatchString(title) {
		errors = append(errors, ValidationError{
			Field:   "title",
			Message: "title contains invalid characters or is too long (max 200 chars)",
		})
	}
	
	// Validate description
	description, ok := req["description"].(string)
	if !ok || description == "" {
		errors = append(errors, ValidationError{
			Field:   "description",
			Message: "description is required",
		})
	} else if len(description) > 5000 {
		errors = append(errors, ValidationError{
			Field:   "description",
			Message: "description too long (max 5000 chars)",
		})
	}
	
	// Validate priority (if provided)
	if priority, exists := req["priority"]; exists {
		if priorityStr, ok := priority.(string); ok {
			validPriorities := []string{"low", "medium", "high", "critical"}
			isValid := false
			for _, valid := range validPriorities {
				if strings.ToLower(priorityStr) == valid {
					isValid = true
					break
				}
			}
			if !isValid {
				errors = append(errors, ValidationError{
					Field:   "priority",
					Message: "priority must be one of: low, medium, high, critical",
				})
			}
		}
	}
	
	return errors
}

// ValidateAgentRequest validates agent registration requests
func ValidateAgentRequest(req map[string]interface{}) ValidationErrors {
	var errors ValidationErrors
	
	// Validate agent_id
	agentID, ok := req["agent_id"].(string)
	if !ok || agentID == "" {
		errors = append(errors, ValidationError{
			Field:   "agent_id",
			Message: "agent_id is required",
		})
	} else if !AgentID.MatchString(agentID) {
		errors = append(errors, ValidationError{
			Field:   "agent_id",
			Message: "agent_id contains invalid characters or is too long (max 50 chars)",
		})
	}
	
	// Validate capabilities (if provided)
	if capabilities, exists := req["capabilities"]; exists {
		if capArray, ok := capabilities.([]interface{}); ok {
			if len(capArray) > 50 {
				errors = append(errors, ValidationError{
					Field:   "capabilities",
					Message: "too many capabilities (max 50)",
				})
			}
			for i, cap := range capArray {
				if capStr, ok := cap.(string); !ok || len(capStr) > 100 {
					errors = append(errors, ValidationError{
						Field:   fmt.Sprintf("capabilities[%d]", i),
						Message: "capability must be string with max 100 chars",
					})
				}
			}
		}
	}
	
	return errors
}

// ValidatePathParameter validates URL path parameters
func ValidatePathParameter(param, value, paramType string) error {
	if value == "" {
		return fmt.Errorf("%s is required", param)
	}
	
	switch paramType {
	case "uuid":
		if !UUID.MatchString(value) {
			return fmt.Errorf("invalid %s format (must be UUID)", param)
		}
	case "alphanumeric":
		if !AlphaNumeric.MatchString(value) {
			return fmt.Errorf("invalid %s format (alphanumeric only)", param)
		}
	case "agent_id":
		if !AgentID.MatchString(value) {
			return fmt.Errorf("invalid %s format", param)
		}
	}
	
	return nil
}

// SanitizeString removes potentially dangerous characters
func SanitizeString(input string) string {
	// Remove null bytes
	input = strings.ReplaceAll(input, "\x00", "")
	
	// Trim whitespace
	input = strings.TrimSpace(input)
	
	return input
}

// ValidateAndRespond validates data and responds with errors if validation fails
func (v *Validator) ValidateAndRespond(w http.ResponseWriter, r *http.Request, errors ValidationErrors) bool {
	if len(errors) > 0 {
		log.Warn().Interface("errors", errors).Msg("Validation failed")
		render.Status(r, http.StatusBadRequest)
		render.JSON(w, r, map[string]interface{}{
			"error":  "validation failed",
			"errors": errors,
		})
		return false
	}
	return true
}

// SecurityHeaders adds security headers to the response
func SecurityHeaders(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Content Security Policy
		w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'")
		
		// X-Frame-Options
		w.Header().Set("X-Frame-Options", "DENY")
		
		// X-Content-Type-Options
		w.Header().Set("X-Content-Type-Options", "nosniff")
		
		// X-XSS-Protection
		w.Header().Set("X-XSS-Protection", "1; mode=block")
		
		// Referrer Policy
		w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
		
		next.ServeHTTP(w, r)
	})
}