// 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.

// Code generated by the Google Gen AI SDK generator DO NOT EDIT.

package genai

import (
	"context"
	"fmt"
	"io"
	"iter"
	"mime"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

func listFilesConfigToMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromPageSize := getValueByPath(fromObject, []string{"pageSize"})
	if fromPageSize != nil {
		setValueByPath(parentObject, []string{"_query", "pageSize"}, fromPageSize)
	}

	fromPageToken := getValueByPath(fromObject, []string{"pageToken"})
	if fromPageToken != nil {
		setValueByPath(parentObject, []string{"_query", "pageToken"}, fromPageToken)
	}

	return toObject, nil
}

func listFilesParametersToMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromConfig := getValueByPath(fromObject, []string{"config"})
	if fromConfig != nil {
		fromConfig, err = listFilesConfigToMldev(ac, fromConfig.(map[string]any), toObject)
		if err != nil {
			return nil, err
		}

		setValueByPath(toObject, []string{"config"}, fromConfig)
	}

	return toObject, nil
}

func fileStatusToMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromDetails := getValueByPath(fromObject, []string{"details"})
	if fromDetails != nil {
		setValueByPath(toObject, []string{"details"}, fromDetails)
	}

	fromMessage := getValueByPath(fromObject, []string{"message"})
	if fromMessage != nil {
		setValueByPath(toObject, []string{"message"}, fromMessage)
	}

	fromCode := getValueByPath(fromObject, []string{"code"})
	if fromCode != nil {
		setValueByPath(toObject, []string{"code"}, fromCode)
	}

	return toObject, nil
}

func fileToMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromName := getValueByPath(fromObject, []string{"name"})
	if fromName != nil {
		setValueByPath(toObject, []string{"name"}, fromName)
	}

	fromDisplayName := getValueByPath(fromObject, []string{"displayName"})
	if fromDisplayName != nil {
		setValueByPath(toObject, []string{"displayName"}, fromDisplayName)
	}

	fromMimeType := getValueByPath(fromObject, []string{"mimeType"})
	if fromMimeType != nil {
		setValueByPath(toObject, []string{"mimeType"}, fromMimeType)
	}

	fromSizeBytes := getValueByPath(fromObject, []string{"sizeBytes"})
	if fromSizeBytes != nil {
		setValueByPath(toObject, []string{"sizeBytes"}, fromSizeBytes)
	}

	fromCreateTime := getValueByPath(fromObject, []string{"createTime"})
	if fromCreateTime != nil {
		setValueByPath(toObject, []string{"createTime"}, fromCreateTime)
	}

	fromExpirationTime := getValueByPath(fromObject, []string{"expirationTime"})
	if fromExpirationTime != nil {
		setValueByPath(toObject, []string{"expirationTime"}, fromExpirationTime)
	}

	fromUpdateTime := getValueByPath(fromObject, []string{"updateTime"})
	if fromUpdateTime != nil {
		setValueByPath(toObject, []string{"updateTime"}, fromUpdateTime)
	}

	fromSha256Hash := getValueByPath(fromObject, []string{"sha256Hash"})
	if fromSha256Hash != nil {
		setValueByPath(toObject, []string{"sha256Hash"}, fromSha256Hash)
	}

	fromUri := getValueByPath(fromObject, []string{"uri"})
	if fromUri != nil {
		setValueByPath(toObject, []string{"uri"}, fromUri)
	}

	fromDownloadUri := getValueByPath(fromObject, []string{"downloadUri"})
	if fromDownloadUri != nil {
		setValueByPath(toObject, []string{"downloadUri"}, fromDownloadUri)
	}

	fromState := getValueByPath(fromObject, []string{"state"})
	if fromState != nil {
		setValueByPath(toObject, []string{"state"}, fromState)
	}

	fromSource := getValueByPath(fromObject, []string{"source"})
	if fromSource != nil {
		setValueByPath(toObject, []string{"source"}, fromSource)
	}

	fromVideoMetadata := getValueByPath(fromObject, []string{"videoMetadata"})
	if fromVideoMetadata != nil {
		setValueByPath(toObject, []string{"videoMetadata"}, fromVideoMetadata)
	}

	fromError := getValueByPath(fromObject, []string{"error"})
	if fromError != nil {
		fromError, err = fileStatusToMldev(ac, fromError.(map[string]any), toObject)
		if err != nil {
			return nil, err
		}

		setValueByPath(toObject, []string{"error"}, fromError)
	}

	return toObject, nil
}

func createFileParametersToMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromFile := getValueByPath(fromObject, []string{"file"})
	if fromFile != nil {
		fromFile, err = fileToMldev(ac, fromFile.(map[string]any), toObject)
		if err != nil {
			return nil, err
		}

		setValueByPath(toObject, []string{"file"}, fromFile)
	}

	fromConfig := getValueByPath(fromObject, []string{"config"})
	if fromConfig != nil {
		setValueByPath(toObject, []string{"config"}, fromConfig)
	}

	return toObject, nil
}

func getFileParametersToMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromName := getValueByPath(fromObject, []string{"name"})
	if fromName != nil {
		fromName, err = tFileName(ac, fromName)
		if err != nil {
			return nil, err
		}

		setValueByPath(toObject, []string{"_url", "file"}, fromName)
	}

	fromConfig := getValueByPath(fromObject, []string{"config"})
	if fromConfig != nil {
		setValueByPath(toObject, []string{"config"}, fromConfig)
	}

	return toObject, nil
}

func deleteFileParametersToMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromName := getValueByPath(fromObject, []string{"name"})
	if fromName != nil {
		fromName, err = tFileName(ac, fromName)
		if err != nil {
			return nil, err
		}

		setValueByPath(toObject, []string{"_url", "file"}, fromName)
	}

	fromConfig := getValueByPath(fromObject, []string{"config"})
	if fromConfig != nil {
		setValueByPath(toObject, []string{"config"}, fromConfig)
	}

	return toObject, nil
}

func fileStatusFromMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromDetails := getValueByPath(fromObject, []string{"details"})
	if fromDetails != nil {
		setValueByPath(toObject, []string{"details"}, fromDetails)
	}

	fromMessage := getValueByPath(fromObject, []string{"message"})
	if fromMessage != nil {
		setValueByPath(toObject, []string{"message"}, fromMessage)
	}

	fromCode := getValueByPath(fromObject, []string{"code"})
	if fromCode != nil {
		setValueByPath(toObject, []string{"code"}, fromCode)
	}

	return toObject, nil
}

func fileFromMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromName := getValueByPath(fromObject, []string{"name"})
	if fromName != nil {
		setValueByPath(toObject, []string{"name"}, fromName)
	}

	fromDisplayName := getValueByPath(fromObject, []string{"displayName"})
	if fromDisplayName != nil {
		setValueByPath(toObject, []string{"displayName"}, fromDisplayName)
	}

	fromMimeType := getValueByPath(fromObject, []string{"mimeType"})
	if fromMimeType != nil {
		setValueByPath(toObject, []string{"mimeType"}, fromMimeType)
	}

	fromSizeBytes := getValueByPath(fromObject, []string{"sizeBytes"})
	if fromSizeBytes != nil {
		setValueByPath(toObject, []string{"sizeBytes"}, fromSizeBytes)
	}

	fromCreateTime := getValueByPath(fromObject, []string{"createTime"})
	if fromCreateTime != nil {
		setValueByPath(toObject, []string{"createTime"}, fromCreateTime)
	}

	fromExpirationTime := getValueByPath(fromObject, []string{"expirationTime"})
	if fromExpirationTime != nil {
		setValueByPath(toObject, []string{"expirationTime"}, fromExpirationTime)
	}

	fromUpdateTime := getValueByPath(fromObject, []string{"updateTime"})
	if fromUpdateTime != nil {
		setValueByPath(toObject, []string{"updateTime"}, fromUpdateTime)
	}

	fromSha256Hash := getValueByPath(fromObject, []string{"sha256Hash"})
	if fromSha256Hash != nil {
		setValueByPath(toObject, []string{"sha256Hash"}, fromSha256Hash)
	}

	fromUri := getValueByPath(fromObject, []string{"uri"})
	if fromUri != nil {
		setValueByPath(toObject, []string{"uri"}, fromUri)
	}

	fromDownloadUri := getValueByPath(fromObject, []string{"downloadUri"})
	if fromDownloadUri != nil {
		setValueByPath(toObject, []string{"downloadUri"}, fromDownloadUri)
	}

	fromState := getValueByPath(fromObject, []string{"state"})
	if fromState != nil {
		setValueByPath(toObject, []string{"state"}, fromState)
	}

	fromSource := getValueByPath(fromObject, []string{"source"})
	if fromSource != nil {
		setValueByPath(toObject, []string{"source"}, fromSource)
	}

	fromVideoMetadata := getValueByPath(fromObject, []string{"videoMetadata"})
	if fromVideoMetadata != nil {
		setValueByPath(toObject, []string{"videoMetadata"}, fromVideoMetadata)
	}

	fromError := getValueByPath(fromObject, []string{"error"})
	if fromError != nil {
		fromError, err = fileStatusFromMldev(ac, fromError.(map[string]any), toObject)
		if err != nil {
			return nil, err
		}

		setValueByPath(toObject, []string{"error"}, fromError)
	}

	return toObject, nil
}

func listFilesResponseFromMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromNextPageToken := getValueByPath(fromObject, []string{"nextPageToken"})
	if fromNextPageToken != nil {
		setValueByPath(toObject, []string{"nextPageToken"}, fromNextPageToken)
	}

	fromFiles := getValueByPath(fromObject, []string{"files"})
	if fromFiles != nil {
		fromFiles, err = applyConverterToSlice(ac, fromFiles.([]any), fileFromMldev)
		if err != nil {
			return nil, err
		}

		setValueByPath(toObject, []string{"files"}, fromFiles)
	}

	return toObject, nil
}

func createFileResponseFromMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	fromHttpHeaders := getValueByPath(fromObject, []string{"httpHeaders"})
	if fromHttpHeaders != nil {
		setValueByPath(toObject, []string{"httpHeaders"}, fromHttpHeaders)
	}

	return toObject, nil
}

func deleteFileResponseFromMldev(ac *apiClient, fromObject map[string]any, parentObject map[string]any) (toObject map[string]any, err error) {
	toObject = make(map[string]any)

	return toObject, nil
}

type Files struct {
	apiClient *apiClient
}

func (m Files) list(ctx context.Context, config *ListFilesConfig) (*ListFilesResponse, error) {
	parameterMap := make(map[string]any)

	kwargs := map[string]any{"config": config}
	deepMarshal(kwargs, &parameterMap)

	var httpOptions *HTTPOptions
	if config == nil {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, nil)
	} else {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, config.HTTPOptions)
		config.HTTPOptions = nil
	}
	var response = new(ListFilesResponse)
	var responseMap map[string]any
	var fromConverter func(*apiClient, map[string]any, map[string]any) (map[string]any, error)
	var toConverter func(*apiClient, map[string]any, map[string]any) (map[string]any, error)
	if m.apiClient.clientConfig.Backend == BackendVertexAI {

		return nil, fmt.Errorf("method List is only supported in the Gemini Developer client. You can choose to use Gemini Developer client by setting ClientConfig.Backend to BackendGeminiAPI.")

	} else {
		toConverter = listFilesParametersToMldev
		fromConverter = listFilesResponseFromMldev
	}

	body, err := toConverter(m.apiClient, parameterMap, nil)
	if err != nil {
		return nil, err
	}
	var path string
	var urlParams map[string]any
	if _, ok := body["_url"]; ok {
		urlParams = body["_url"].(map[string]any)
		delete(body, "_url")
	}
	if m.apiClient.clientConfig.Backend == BackendVertexAI {
		path, err = formatMap("None", urlParams)
	} else {
		path, err = formatMap("files", urlParams)
	}
	if err != nil {
		return nil, fmt.Errorf("invalid url params: %#v.\n%w", urlParams, err)
	}
	if _, ok := body["_query"]; ok {
		query, err := createURLQuery(body["_query"].(map[string]any))
		if err != nil {
			return nil, err
		}
		path += "?" + query
		delete(body, "_query")
	}

	if _, ok := body["config"]; ok {
		delete(body, "config")
	}
	responseMap, err = sendRequest(ctx, m.apiClient, path, http.MethodGet, body, httpOptions)
	if err != nil {
		return nil, err
	}
	responseMap, err = fromConverter(m.apiClient, responseMap, nil)
	if err != nil {
		return nil, err
	}
	err = mapToStruct(responseMap, response)
	if err != nil {
		return nil, err
	}
	return response, nil
}

func (m Files) create(ctx context.Context, file *File, config *CreateFileConfig) (*CreateFileResponse, error) {
	parameterMap := make(map[string]any)

	kwargs := map[string]any{"file": file, "config": config}
	deepMarshal(kwargs, &parameterMap)

	var httpOptions *HTTPOptions
	if config == nil {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, nil)
	} else {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, config.HTTPOptions)
		config.HTTPOptions = nil
	}
	var response = new(CreateFileResponse)
	var responseMap map[string]any
	var fromConverter func(*apiClient, map[string]any, map[string]any) (map[string]any, error)
	var toConverter func(*apiClient, map[string]any, map[string]any) (map[string]any, error)
	if m.apiClient.clientConfig.Backend == BackendVertexAI {

		return nil, fmt.Errorf("method Create is only supported in the Gemini Developer client. You can choose to use Gemini Developer client by setting ClientConfig.Backend to BackendGeminiAPI.")

	} else {
		toConverter = createFileParametersToMldev
		fromConverter = createFileResponseFromMldev
	}

	body, err := toConverter(m.apiClient, parameterMap, nil)
	if err != nil {
		return nil, err
	}
	var path string
	var urlParams map[string]any
	if _, ok := body["_url"]; ok {
		urlParams = body["_url"].(map[string]any)
		delete(body, "_url")
	}
	if m.apiClient.clientConfig.Backend == BackendVertexAI {
		path, err = formatMap("None", urlParams)
	} else {
		path, err = formatMap("upload/v1beta/files", urlParams)
	}
	if err != nil {
		return nil, fmt.Errorf("invalid url params: %#v.\n%w", urlParams, err)
	}
	if _, ok := body["_query"]; ok {
		query, err := createURLQuery(body["_query"].(map[string]any))
		if err != nil {
			return nil, err
		}
		path += "?" + query
		delete(body, "_query")
	}

	if _, ok := body["config"]; ok {
		delete(body, "config")
	}
	responseMap, err = sendRequest(ctx, m.apiClient, path, http.MethodPost, body, httpOptions)
	if err != nil {
		return nil, err
	}
	responseMap, err = fromConverter(m.apiClient, responseMap, nil)
	if err != nil {
		return nil, err
	}
	err = mapToStruct(responseMap, response)
	if err != nil {
		return nil, err
	}
	return response, nil
}

func (m Files) Get(ctx context.Context, name string, config *GetFileConfig) (*File, error) {
	parameterMap := make(map[string]any)

	kwargs := map[string]any{"name": name, "config": config}
	deepMarshal(kwargs, &parameterMap)

	var httpOptions *HTTPOptions
	if config == nil {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, nil)
	} else {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, config.HTTPOptions)
		config.HTTPOptions = nil
	}
	var response = new(File)
	var responseMap map[string]any
	var fromConverter func(*apiClient, map[string]any, map[string]any) (map[string]any, error)
	var toConverter func(*apiClient, map[string]any, map[string]any) (map[string]any, error)
	if m.apiClient.clientConfig.Backend == BackendVertexAI {

		return nil, fmt.Errorf("method Get is only supported in the Gemini Developer client. You can choose to use Gemini Developer client by setting ClientConfig.Backend to BackendGeminiAPI.")

	} else {
		toConverter = getFileParametersToMldev
		fromConverter = fileFromMldev
	}

	body, err := toConverter(m.apiClient, parameterMap, nil)
	if err != nil {
		return nil, err
	}
	var path string
	var urlParams map[string]any
	if _, ok := body["_url"]; ok {
		urlParams = body["_url"].(map[string]any)
		delete(body, "_url")
	}
	if m.apiClient.clientConfig.Backend == BackendVertexAI {
		path, err = formatMap("None", urlParams)
	} else {
		path, err = formatMap("files/{file}", urlParams)
	}
	if err != nil {
		return nil, fmt.Errorf("invalid url params: %#v.\n%w", urlParams, err)
	}
	if _, ok := body["_query"]; ok {
		query, err := createURLQuery(body["_query"].(map[string]any))
		if err != nil {
			return nil, err
		}
		path += "?" + query
		delete(body, "_query")
	}

	if _, ok := body["config"]; ok {
		delete(body, "config")
	}
	responseMap, err = sendRequest(ctx, m.apiClient, path, http.MethodGet, body, httpOptions)
	if err != nil {
		return nil, err
	}
	responseMap, err = fromConverter(m.apiClient, responseMap, nil)
	if err != nil {
		return nil, err
	}
	err = mapToStruct(responseMap, response)
	if err != nil {
		return nil, err
	}
	return response, nil
}

func (m Files) Delete(ctx context.Context, name string, config *DeleteFileConfig) (*DeleteFileResponse, error) {
	parameterMap := make(map[string]any)

	kwargs := map[string]any{"name": name, "config": config}
	deepMarshal(kwargs, &parameterMap)

	var httpOptions *HTTPOptions
	if config == nil {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, nil)
	} else {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, config.HTTPOptions)
		config.HTTPOptions = nil
	}
	var response = new(DeleteFileResponse)
	var responseMap map[string]any
	var fromConverter func(*apiClient, map[string]any, map[string]any) (map[string]any, error)
	var toConverter func(*apiClient, map[string]any, map[string]any) (map[string]any, error)
	if m.apiClient.clientConfig.Backend == BackendVertexAI {

		return nil, fmt.Errorf("method Delete is only supported in the Gemini Developer client. You can choose to use Gemini Developer client by setting ClientConfig.Backend to BackendGeminiAPI.")

	} else {
		toConverter = deleteFileParametersToMldev
		fromConverter = deleteFileResponseFromMldev
	}

	body, err := toConverter(m.apiClient, parameterMap, nil)
	if err != nil {
		return nil, err
	}
	var path string
	var urlParams map[string]any
	if _, ok := body["_url"]; ok {
		urlParams = body["_url"].(map[string]any)
		delete(body, "_url")
	}
	if m.apiClient.clientConfig.Backend == BackendVertexAI {
		path, err = formatMap("None", urlParams)
	} else {
		path, err = formatMap("files/{file}", urlParams)
	}
	if err != nil {
		return nil, fmt.Errorf("invalid url params: %#v.\n%w", urlParams, err)
	}
	if _, ok := body["_query"]; ok {
		query, err := createURLQuery(body["_query"].(map[string]any))
		if err != nil {
			return nil, err
		}
		path += "?" + query
		delete(body, "_query")
	}

	if _, ok := body["config"]; ok {
		delete(body, "config")
	}
	responseMap, err = sendRequest(ctx, m.apiClient, path, http.MethodDelete, body, httpOptions)
	if err != nil {
		return nil, err
	}
	responseMap, err = fromConverter(m.apiClient, responseMap, nil)
	if err != nil {
		return nil, err
	}
	err = mapToStruct(responseMap, response)
	if err != nil {
		return nil, err
	}
	return response, nil
}

// List retrieves a paginated list of files resources.
func (m Files) List(ctx context.Context, config *ListFilesConfig) (Page[File], error) {
	listFunc := func(ctx context.Context, config map[string]any) ([]*File, string, error) {
		var c ListFilesConfig
		if err := mapToStruct(config, &c); err != nil {
			return nil, "", err
		}
		resp, err := m.list(ctx, &c)
		if err != nil {
			return nil, "", err
		}
		return resp.Files, resp.NextPageToken, nil
	}
	c := make(map[string]any)
	deepMarshal(config, &c)
	return newPage(ctx, "files", c, listFunc)
}

// All retrieves all files resources.
//
// This method handles pagination internally, making multiple API calls as needed
// to fetch all entries. It returns an iterator that yields each file
// entry one by one. You do not need to manage pagination
// tokens or make multiple calls to retrieve all data.
func (m Files) All(ctx context.Context) iter.Seq2[*File, error] {
	listFunc := func(ctx context.Context, config map[string]any) ([]*File, string, error) {
		var c ListFilesConfig
		if err := mapToStruct(config, &c); err != nil {
			return nil, "", err
		}
		resp, err := m.list(ctx, &c)
		if err != nil {
			return nil, "", err
		}
		return resp.Files, resp.NextPageToken, nil
	}
	p, err := newPage(ctx, "files", map[string]any{}, listFunc)
	if err != nil {
		return yieldErrorAndEndIterator[File](err)
	}
	return p.all(ctx)
}

// Download function downloads a file from the specified URI.
// If the URI refers to a video([Video], [GeneratedVideo]), the video bytes will be populated to the video's VideoBytes field.
func (m Files) Download(ctx context.Context, uri DownloadURI, config *DownloadFileConfig) ([]byte, error) {
	if m.apiClient.clientConfig.Backend == BackendVertexAI {
		return nil, fmt.Errorf("method Download is only supported in the Gemini Developer client. You can choose to use Gemini Developer client by setting ClientConfig.Backend to BackendGeminiAPI.")
	}
	if uri.uri() == "" {
		return nil, fmt.Errorf("the resource doesn't support download")
	}
	fileName, err := tFileName(m.apiClient, uri.uri())
	if err != nil {
		return nil, err
	}
	path := fmt.Sprintf("files/%s:download?alt=media", fileName)

	var httpOptions *HTTPOptions
	if config == nil {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, nil)
	} else {
		httpOptions = mergeHTTPOptions(m.apiClient.clientConfig, config.HTTPOptions)
		config.HTTPOptions = nil
	}

	data, err := downloadFile(ctx, m.apiClient, path, httpOptions)
	if err != nil {
		return nil, err
	}
	_ = uri.setVideoBytes(data)
	return data, nil
}

// Upload copies the contents of the given io.Reader to file storage associated
// with the service, and returns information about the resulting file.
func (m Files) Upload(ctx context.Context, r io.Reader, config *UploadFileConfig) (*File, error) {
	if m.apiClient.clientConfig.Backend == BackendVertexAI {
		return nil, fmt.Errorf("This method is only supported in the Gemini Developer client.")
	}

	var fileToUpload File
	if config != nil {
		fileToUpload.MIMEType = config.MIMEType
		fileToUpload.Name = config.Name
		fileToUpload.DisplayName = config.DisplayName
	}

	if fileToUpload.Name != "" && !strings.HasPrefix(fileToUpload.Name, "files/") {
		fileToUpload.Name = "files/" + fileToUpload.Name
	}

	httpOptions := HTTPOptions{Headers: http.Header{}}
	if config != nil && config.HTTPOptions != nil {
		deepCopy(*config.HTTPOptions, &httpOptions)
	}
	if httpOptions.Headers == nil {
		httpOptions.Headers = http.Header{}
	}

	httpOptions.APIVersion = ""
	httpOptions.Headers.Add("Content-Type", "application/json")
	httpOptions.Headers.Add("X-Goog-Upload-Protocol", "resumable")
	httpOptions.Headers.Add("X-Goog-Upload-Command", "start")
	httpOptions.Headers.Add("X-Goog-Upload-Header-Content-Type", fileToUpload.MIMEType)

	var createFileConfig CreateFileConfig
	createFileConfig.HTTPOptions = &httpOptions

	resp, err := m.create(ctx, &fileToUpload, &createFileConfig)
	if err != nil {
		return nil, fmt.Errorf("Failed to create file. Ran into an error: %s", err)
	}
	if resp.HTTPHeaders == nil || resp.HTTPHeaders.Get("x-goog-upload-url") == "" {
		return nil, fmt.Errorf("Failed to create file. Upload URL was not returned from the create file request.")
	}

	uploadURL := resp.HTTPHeaders.Get("x-goog-upload-url")
	return m.apiClient.uploadFile(ctx, r, uploadURL, &httpOptions)
}

// UploadFromPath uploads a file from the specified path and returns information
// about the resulting file.
func (m Files) UploadFromPath(ctx context.Context, path string, config *UploadFileConfig) (*File, error) {
	fileInfo, err := os.Stat(path)
	if err != nil || fileInfo.IsDir() {
		return nil, fmt.Errorf("%s is not a valid file path.", path)
	}

	osf, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer osf.Close()

	if config == nil {
		config = &UploadFileConfig{}
	}

	var copiedCfg UploadFileConfig
	deepCopy(*config, &copiedCfg)

	if copiedCfg.MIMEType == "" {
		copiedCfg.MIMEType = mime.TypeByExtension(filepath.Ext(path))
		if copiedCfg.MIMEType == "" {
			return nil, fmt.Errorf("Unknown mime type: Could not determine the mimetype for your file please set the `MIMEType` argument")
		}
	}

	if copiedCfg.HTTPOptions == nil {
		copiedCfg.HTTPOptions = &HTTPOptions{Headers: http.Header{}}
	}
	if copiedCfg.HTTPOptions.Headers == nil {
		copiedCfg.HTTPOptions.Headers = http.Header{}
	}
	copiedCfg.HTTPOptions.Headers.Add("X-Goog-Upload-Header-Content-Length", strconv.FormatInt(fileInfo.Size(), 10))

	return m.Upload(ctx, osf, &copiedCfg)
}
