Support loading multiple mapping files

Change-Id: I3c6caaa3c4c3434dfacfb842f0407250b5e980f0
diff --git a/config/config.go b/config/config.go
index 128c063..46d6639 100644
--- a/config/config.go
+++ b/config/config.go
@@ -34,44 +34,107 @@
 	Lists  []MappingList `yaml:"lists,omitempty"`
 }
 
-// LoadConfig loads a YAML configuration file and returns a Config object
-func LoadConfig(filename string) (*MappingConfig, error) {
-	data, err := os.ReadFile(filename)
-	if err != nil {
-		return nil, fmt.Errorf("failed to read config file: %w", err)
-	}
+// LoadFromSources loads configuration from multiple sources and merges them:
+// - A main configuration file (optional) containing global settings and lists
+// - Individual mapping files (optional) containing single mapping lists each
+// At least one source must be provided
+func LoadFromSources(configFile string, mappingFiles []string) (*MappingConfig, error) {
+	var allLists []MappingList
+	var globalConfig MappingConfig
 
-	// Check for empty file
-	if len(data) == 0 {
-		return nil, fmt.Errorf("EOF: config file is empty")
-	}
+	// Track seen IDs across all sources to detect duplicates
+	seenIDs := make(map[string]bool)
 
-	// Try to unmarshal as new format first (object with optional sdk/server and lists)
-	var config MappingConfig
-	if err := yaml.Unmarshal(data, &config); err == nil && len(config.Lists) > 0 {
-		// Successfully parsed as new format with lists field
-		if err := validateMappingLists(config.Lists); err != nil {
-			return nil, err
+	// Load main configuration file if provided
+	if configFile != "" {
+		data, err := os.ReadFile(configFile)
+		if err != nil {
+			return nil, fmt.Errorf("failed to read config file '%s': %w", configFile, err)
 		}
-		// Apply defaults if not specified
-		applyDefaults(&config)
-		return &config, nil
+
+		if len(data) == 0 {
+			return nil, fmt.Errorf("EOF: config file '%s' is empty", configFile)
+		}
+
+		// Try to unmarshal as new format first (object with optional sdk/server and lists)
+		if err := yaml.Unmarshal(data, &globalConfig); err == nil && len(globalConfig.Lists) > 0 {
+			// Successfully parsed as new format with lists field
+			for _, list := range globalConfig.Lists {
+				if seenIDs[list.ID] {
+					return nil, fmt.Errorf("duplicate mapping list ID found: %s", list.ID)
+				}
+				seenIDs[list.ID] = true
+			}
+			allLists = append(allLists, globalConfig.Lists...)
+		} else {
+			// Fall back to old format (direct list)
+			var lists []MappingList
+			if err := yaml.Unmarshal(data, &lists); err != nil {
+				return nil, fmt.Errorf("failed to parse YAML config file '%s': %w", configFile, err)
+			}
+
+			for _, list := range lists {
+				if seenIDs[list.ID] {
+					return nil, fmt.Errorf("duplicate mapping list ID found: %s", list.ID)
+				}
+				seenIDs[list.ID] = true
+			}
+			allLists = append(allLists, lists...)
+			// Clear the lists from globalConfig since we got them from the old format
+			globalConfig.Lists = nil
+		}
 	}
 
-	// Fall back to old format (direct list)
-	var lists []MappingList
-	if err := yaml.Unmarshal(data, &lists); err != nil {
-		return nil, fmt.Errorf("failed to parse YAML: %w", err)
+	// Load individual mapping files
+	for _, file := range mappingFiles {
+		data, err := os.ReadFile(file)
+		if err != nil {
+			return nil, fmt.Errorf("failed to read mapping file '%s': %w", file, err)
+		}
+
+		if len(data) == 0 {
+			return nil, fmt.Errorf("EOF: mapping file '%s' is empty", file)
+		}
+
+		var list MappingList
+		if err := yaml.Unmarshal(data, &list); err != nil {
+			return nil, fmt.Errorf("failed to parse YAML mapping file '%s': %w", file, err)
+		}
+
+		if seenIDs[list.ID] {
+			return nil, fmt.Errorf("duplicate mapping list ID found: %s", list.ID)
+		}
+		seenIDs[list.ID] = true
+		allLists = append(allLists, list)
 	}
 
-	if err := validateMappingLists(lists); err != nil {
+	// Ensure we have at least some configuration
+	if len(allLists) == 0 {
+		return nil, fmt.Errorf("no mapping lists found: provide either a config file (-c) with lists or mapping files (-m)")
+	}
+
+	// Validate all mapping lists
+	if err := validateMappingLists(allLists); err != nil {
 		return nil, err
 	}
 
-	config = MappingConfig{Lists: lists}
+	// Create final configuration
+	result := &MappingConfig{
+		SDK:    globalConfig.SDK,
+		Server: globalConfig.Server,
+		Lists:  allLists,
+	}
+
 	// Apply defaults if not specified
-	applyDefaults(&config)
-	return &config, nil
+	applyDefaults(result)
+
+	return result, nil
+}
+
+// LoadConfig loads a YAML configuration file and returns a Config object
+// Deprecated: Use LoadFromSources for new code
+func LoadConfig(filename string) (*MappingConfig, error) {
+	return LoadFromSources(filename, nil)
 }
 
 // applyDefaults sets default values for SDK and Server if they are empty