// Package validation provides shared validation utilities for Android package names
// and Java identifier validation across all platform builders.
package validation

import (
	"fmt"
	"strings"
)

// javaKeywords contains all Java keywords (50) and reserved literals (3).
// These cannot be used as package name segments.
var javaKeywords = map[string]bool{
	// Java keywords (50)
	"abstract": true, "assert": true, "boolean": true, "break": true, "byte": true,
	"case": true, "catch": true, "char": true, "class": true, "const": true,
	"continue": true, "default": true, "do": true, "double": true, "else": true,
	"enum": true, "extends": true, "final": true, "finally": true, "float": true,
	"for": true, "goto": true, "if": true, "implements": true, "import": true,
	"instanceof": true, "int": true, "interface": true, "long": true, "native": true,
	"new": true, "package": true, "private": true, "protected": true, "public": true,
	"return": true, "short": true, "static": true, "strictfp": true, "super": true,
	"switch": true, "synchronized": true, "this": true, "throw": true, "throws": true,
	"transient": true, "try": true, "void": true, "volatile": true, "while": true,

	// Java literals (3) - reserved but not technically keywords
	"true": true, "false": true, "null": true,
}

// androidReservedPrefixes contains package prefixes reserved by Android.
// These cannot be used as the first segment of a package name.
var androidReservedPrefixes = map[string]bool{
	"android":  true,
	"androidx": true,
}

// IsJavaKeyword checks if a string is a Java keyword or reserved literal.
// Returns true for all 50 Java keywords plus the 3 literals (true, false, null).
func IsJavaKeyword(s string) bool {
	return javaKeywords[s]
}

// IsAndroidReservedPackage checks if a segment at the given position is Android reserved.
// Android reserved prefixes (android, androidx) are only restricted in the first position (index 0).
// Using these as the first segment would conflict with Android system packages.
func IsAndroidReservedPackage(segment string, position int) bool {
	if position != 0 {
		return false
	}
	return androidReservedPrefixes[segment]
}

// ValidatePackageName validates that a package name follows Java/Android naming conventions.
// It checks for:
// - Non-empty package name
// - At least 2 segments (e.g., com.example)
// - No empty segments
// - No Java keywords or reserved literals
// - No Android reserved prefixes in first position
// - Valid segment format (starts with lowercase letter, contains only lowercase letters, digits, underscores)
func ValidatePackageName(packageName string) error {
	if packageName == "" {
		return fmt.Errorf("package name cannot be empty")
	}

	// Package must contain at least one dot (minimum 2 segments)
	if !strings.Contains(packageName, ".") {
		return fmt.Errorf("package name must have at least 2 segments (e.g., com.example)")
	}

	// Split into segments
	segments := strings.Split(packageName, ".")

	// Validate each segment
	for i, segment := range segments {
		// Check for empty segments (consecutive dots, leading/trailing dots)
		if segment == "" {
			return fmt.Errorf("package name cannot have empty segments")
		}

		// Check if segment is a Java keyword or literal
		if IsJavaKeyword(segment) {
			return fmt.Errorf("package segment '%s' is a Java keyword", segment)
		}

		// Check Android reserved prefixes (first segment only)
		if IsAndroidReservedPackage(segment, i) {
			return fmt.Errorf("package segment '%s' is Android reserved and cannot be used as the first segment", segment)
		}

		// Segment must start with a lowercase letter
		if len(segment) > 0 && !isLowerLetter(rune(segment[0])) {
			return fmt.Errorf("package segment '%s' must start with a letter", segment)
		}

		// All characters must be lowercase letters, digits, or underscores
		for _, char := range segment {
			if !isLowerLetter(char) && !isDigit(char) && char != '_' {
				return fmt.Errorf("package segment '%s' contains invalid character '%c'", segment, char)
			}
		}
	}

	return nil
}

// HasUppercase checks if a package name contains any uppercase letters.
// This is useful for logging warnings since Java convention is to use lowercase
// in package names, though uppercase is technically valid.
func HasUppercase(packageName string) bool {
	for _, char := range packageName {
		if char >= 'A' && char <= 'Z' {
			return true
		}
	}
	return false
}

// isLowerLetter checks if a rune is a lowercase letter (a-z)
func isLowerLetter(r rune) bool {
	return r >= 'a' && r <= 'z'
}

// isDigit checks if a rune is a digit (0-9)
func isDigit(r rune) bool {
	return r >= '0' && r <= '9'
}
