// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package common

import (
	"fmt"
	"slices"
	"strings"

	"github.com/googleapis/genai-toolbox/internal/util/parameters"
	"google.golang.org/api/googleapi"
)

// StoreKey is the key used to identify FHIR/DICOM store IDs in tool parameters.
const StoreKey = "storeID"

// EnablePatientNameFuzzyMatchingKey is the key used for DICOM search to enable
// fuzzy matching.
const EnablePatientNameFuzzyMatchingKey = "fuzzymatching"

// IncludeAttributesKey is the key used for DICOM search to include additional
// tags in the response.
const IncludeAttributesKey = "includefield"

// ValidateAndFetchStoreID validates the provided storeID against the allowedStores.
// If only one store is allowed, it returns that storeID.
// If multiple stores are allowed, it checks if the storeID parameter is in the allowed list.
func ValidateAndFetchStoreID(params parameters.ParamValues, allowedStores map[string]struct{}) (string, error) {
	if len(allowedStores) == 1 {
		for k := range allowedStores {
			return k, nil
		}
	}
	mapParams := params.AsMap()
	storeID, ok := mapParams[StoreKey].(string)
	if !ok {
		return "", fmt.Errorf("invalid or missing '%s' parameter; expected a string", StoreKey)
	}
	if len(allowedStores) > 0 {
		if _, ok := allowedStores[storeID]; !ok {
			return "", fmt.Errorf("store ID '%s' is not in the list of allowed stores", storeID)
		}
	}
	return storeID, nil
}

// ParseDICOMSearchParameters extracts the search parameters for various DICOM
// search methods.
func ParseDICOMSearchParameters(params parameters.ParamValues, paramKeys []string) ([]googleapi.CallOption, error) {
	var opts []googleapi.CallOption
	for k, v := range params.AsMap() {
		if k == IncludeAttributesKey {
			if _, ok := v.([]any); !ok {
				return nil, fmt.Errorf("invalid '%s' parameter; expected a string array", k)
			}
			attributeIDsSlice, err := parameters.ConvertAnySliceToTyped(v.([]any), "string")
			if err != nil {
				return nil, fmt.Errorf("can't convert '%s' to array of strings: %s", k, err)
			}
			attributeIDs := attributeIDsSlice.([]string)
			if len(attributeIDs) != 0 {
				opts = append(opts, googleapi.QueryParameter(k, strings.Join(attributeIDs, ",")))
			}
		} else if k == EnablePatientNameFuzzyMatchingKey {
			if _, ok := v.(bool); !ok {
				return nil, fmt.Errorf("invalid '%s' parameter; expected a boolean", k)
			}
			opts = append(opts, googleapi.QueryParameter(k, fmt.Sprintf("%t", v.(bool))))
		} else if slices.Contains(paramKeys, k) {
			if _, ok := v.(string); !ok {
				return nil, fmt.Errorf("invalid '%s' parameter; expected a string", k)
			}
			if v.(string) != "" {
				opts = append(opts, googleapi.QueryParameter(k, v.(string)))
			}
		}
	}
	return opts, nil
}
