// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#include "azure/storage/files/shares/share_service_client.hpp"

#include "azure/storage/files/shares/share_client.hpp"
#include "private/package_version.hpp"

#include <azure/core/credentials/credentials.hpp>
#include <azure/core/http/policies/policy.hpp>
#include <azure/storage/common/crypt.hpp>
#include <azure/storage/common/internal/constants.hpp>
#include <azure/storage/common/internal/shared_key_policy.hpp>
#include <azure/storage/common/internal/storage_pipeline.hpp>
#include <azure/storage/common/storage_common.hpp>
#include <azure/storage/common/storage_credential.hpp>

namespace Azure { namespace Storage { namespace Files { namespace Shares {
  ShareServiceClient ShareServiceClient::CreateFromConnectionString(
      const std::string& connectionString,
      const ShareClientOptions& options)
  {
    auto parsedConnectionString = _internal::ParseConnectionString(connectionString);
    auto serviceUrl = std::move(parsedConnectionString.FileServiceUrl);

    if (parsedConnectionString.KeyCredential)
    {
      return ShareServiceClient(
          serviceUrl.GetAbsoluteUrl(), parsedConnectionString.KeyCredential, options);
    }
    else
    {
      return ShareServiceClient(serviceUrl.GetAbsoluteUrl(), options);
    }
  }

  ShareServiceClient::ShareServiceClient(
      const std::string& serviceUrl,
      std::shared_ptr<StorageSharedKeyCredential> credential,
      const ShareClientOptions& options)
      : m_serviceUrl(serviceUrl)
  {
    m_clientConfiguration.AllowTrailingDot = options.AllowTrailingDot;
    m_clientConfiguration.AllowSourceTrailingDot = options.AllowSourceTrailingDot;
    m_clientConfiguration.ShareTokenIntent = options.ShareTokenIntent;
    m_clientConfiguration.UploadValidationOptions = options.UploadValidationOptions;
    m_clientConfiguration.DownloadValidationOptions = options.DownloadValidationOptions;

    _internal::BuildStoragePipelineOptions pipelineOptions;
    pipelineOptions.PackageName = _internal::FileServicePackageName;
    pipelineOptions.PackageVersion = _detail::PackageVersion::ToString();
    pipelineOptions.PrimaryHost = m_serviceUrl.GetHost();
    pipelineOptions.ApiVersion = options.ApiVersion;
    pipelineOptions.SharedKeyAuthPolicy = std::make_unique<_internal::SharedKeyPolicy>(credential);

    m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
        _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions)));
  }

  ShareServiceClient::ShareServiceClient(
      const std::string& serviceUrl,
      std::shared_ptr<const Core::Credentials::TokenCredential> credential,
      const ShareClientOptions& options)
      : m_serviceUrl(serviceUrl)
  {
    m_clientConfiguration.AllowTrailingDot = options.AllowTrailingDot;
    m_clientConfiguration.AllowSourceTrailingDot = options.AllowSourceTrailingDot;
    m_clientConfiguration.ShareTokenIntent = options.ShareTokenIntent;
    m_clientConfiguration.UploadValidationOptions = options.UploadValidationOptions;
    m_clientConfiguration.DownloadValidationOptions = options.DownloadValidationOptions;

    _internal::BuildStoragePipelineOptions pipelineOptions;
    pipelineOptions.PackageName = _internal::FileServicePackageName;
    pipelineOptions.PackageVersion = _detail::PackageVersion::ToString();
    pipelineOptions.PrimaryHost = m_serviceUrl.GetHost();
    pipelineOptions.ApiVersion = options.ApiVersion;
    {
      Azure::Core::Credentials::TokenRequestContext tokenContext;
      tokenContext.Scopes.emplace_back(
          options.Audience.HasValue()
              ? _internal::GetDefaultScopeForAudience(options.Audience.Value().ToString())
              : _internal::StorageScope);
      pipelineOptions.TokenAuthPolicy = std::make_unique<
          Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
          credential, tokenContext);
    }

    m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
        _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions)));
  }

  ShareServiceClient::ShareServiceClient(
      const std::string& serviceUrl,
      const ShareClientOptions& options)
      : m_serviceUrl(serviceUrl)
  {
    m_clientConfiguration.AllowTrailingDot = options.AllowTrailingDot;
    m_clientConfiguration.AllowSourceTrailingDot = options.AllowSourceTrailingDot;
    m_clientConfiguration.ShareTokenIntent = options.ShareTokenIntent;
    m_clientConfiguration.UploadValidationOptions = options.UploadValidationOptions;
    m_clientConfiguration.DownloadValidationOptions = options.DownloadValidationOptions;

    _internal::BuildStoragePipelineOptions pipelineOptions;
    pipelineOptions.PackageName = _internal::FileServicePackageName;
    pipelineOptions.PackageVersion = _detail::PackageVersion::ToString();
    pipelineOptions.PrimaryHost = m_serviceUrl.GetHost();
    pipelineOptions.ApiVersion = options.ApiVersion;

    m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
        _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions)));
  }

  ShareClient ShareServiceClient::GetShareClient(const std::string& shareName) const
  {
    auto builder = m_serviceUrl;
    builder.AppendPath(_internal::UrlEncodePath(shareName));
    return ShareClient(builder, m_pipeline, m_clientConfiguration);
  }

  ListSharesPagedResponse ShareServiceClient::ListShares(
      const ListSharesOptions& options,
      const Azure::Core::Context& context) const
  {
    auto protocolLayerOptions = _detail::ServiceClient::ListServiceSharesSegmentOptions();
    protocolLayerOptions.Include = options.ListSharesIncludeFlags;
    protocolLayerOptions.Marker = options.ContinuationToken;
    protocolLayerOptions.MaxResults = options.PageSizeHint;
    protocolLayerOptions.Prefix = options.Prefix;
    protocolLayerOptions.FileRequestIntent = m_clientConfiguration.ShareTokenIntent;
    auto response = _detail::ServiceClient::ListSharesSegment(
        *m_pipeline, m_serviceUrl, protocolLayerOptions, context);

    ListSharesPagedResponse pagedResponse;
    pagedResponse.ServiceEndpoint = std::move(response.Value.ServiceEndpoint);
    pagedResponse.Prefix = response.Value.Prefix.ValueOr(std::string());
    pagedResponse.Shares = std::move(response.Value.ShareItems);
    pagedResponse.m_shareServiceClient = std::make_shared<ShareServiceClient>(*this);
    pagedResponse.m_operationOptions = options;
    pagedResponse.CurrentPageToken = options.ContinuationToken.ValueOr(std::string());
    if (!response.Value.NextMarker.empty())
    {
      pagedResponse.NextPageToken = response.Value.NextMarker;
    }
    pagedResponse.RawResponse = std::move(response.RawResponse);

    return pagedResponse;
  }

  Azure::Response<Models::SetServicePropertiesResult> ShareServiceClient::SetProperties(
      Models::ShareServiceProperties properties,
      const SetServicePropertiesOptions& options,
      const Azure::Core::Context& context) const
  {
    (void)options;
    auto protocolLayerOptions = _detail::ServiceClient::SetServicePropertiesOptions();
    protocolLayerOptions.ShareServiceProperties = std::move(properties);
    protocolLayerOptions.FileRequestIntent = m_clientConfiguration.ShareTokenIntent;
    return _detail::ServiceClient::SetProperties(
        *m_pipeline, m_serviceUrl, protocolLayerOptions, context);
  }

  Azure::Response<Models::ShareServiceProperties> ShareServiceClient::GetProperties(
      const GetServicePropertiesOptions& options,
      const Azure::Core::Context& context) const
  {
    (void)options;
    auto protocolLayerOptions = _detail::ServiceClient::GetServicePropertiesOptions();
    protocolLayerOptions.FileRequestIntent = m_clientConfiguration.ShareTokenIntent;
    auto result = _detail::ServiceClient::GetProperties(
        *m_pipeline, m_serviceUrl, protocolLayerOptions, context);
    Models::ShareServiceProperties ret;
    ret.Cors = std::move(result.Value.Cors);
    ret.HourMetrics = std::move(result.Value.HourMetrics);
    ret.MinuteMetrics = std::move(result.Value.MinuteMetrics);
    ret.Protocol = std::move(result.Value.Protocol);
    return Azure::Response<Models::ShareServiceProperties>(
        std::move(ret), std::move(result.RawResponse));
  }

  Azure::Response<Models::UserDelegationKey> ShareServiceClient::GetUserDelegationKey(
      const Azure::DateTime& expiresOn,
      const GetUserDelegationKeyOptions& options,
      const Azure::Core::Context& context) const
  {
    _detail::ServiceClient::GetServiceUserDelegationKeyOptions protocolLayerOptions;
    protocolLayerOptions.KeyInfo.Start = options.StartsOn.ToString(
        Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate);
    protocolLayerOptions.KeyInfo.Expiry = expiresOn.ToString(
        Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate);
    protocolLayerOptions.KeyInfo.DelegatedUserTid = options.DelegatedUserTid;
    return _detail::ServiceClient::GetUserDelegationKey(
        *m_pipeline, m_serviceUrl, protocolLayerOptions, context);
  }

}}}} // namespace Azure::Storage::Files::Shares
