# frozen_string_literal: true

module Fog
  module Storage
    class Aliyun
      class Real
        # Put details for object
        #
        # ==== Parameters
        # * object<~String> - Name of object to look for
        #
        def put_object(object, file = nil, options = {})
          bucket = options[:bucket]
          bucket ||= @aliyun_oss_bucket
          location = get_bucket_location(bucket)
          endpoint = 'http://' + location + '.aliyuncs.com'
          return put_folder(bucket, object, endpoint) if file.nil?

          # put multiparts if object's size is over 100m
          return put_multipart_object(bucket, object, file) if file.size > 104_857_600

          body = file.read

          resource = bucket + '/' + object
          ret = request(
            expects: [200, 203],
            method: 'PUT',
            path: object,
            bucket: bucket,
            resource: resource,
            body: body,
            endpoint: endpoint
          )
        end

        def put_object_with_body(object, body, options = {})
          bucket = options[:bucket]
          bucket ||= @aliyun_oss_bucket
          location = get_bucket_location(bucket)
          endpoint = 'http://' + location + '.aliyuncs.com'

          resource = bucket + '/' + object
          ret = request(
            expects: [200, 203],
            method: 'PUT',
            path: object,
            bucket: bucket,
            resource: resource,
            body: body,
            endpoint: endpoint
          )
      end

        def put_folder(bucket, folder, endpoint)
          if endpoint.nil?
            location = get_bucket_location(bucket)
            endpoint = 'http://' + location + '.aliyuncs.com'
          end
          path = folder + '/'
          resource = bucket + '/' + folder + '/'
          ret = request(
            expects: [200, 203],
            method: 'PUT',
            path: path,
            bucket: bucket,
            resource: resource,
            endpoint: endpoint
          )
        end

        def put_multipart_object(bucket, object, file)
          location = get_bucket_location(bucket)
          endpoint = 'http://' + location + '.aliyuncs.com'

          # find the right uploadid
          uploads = list_multipart_uploads(bucket, endpoint)
          upload = (uploads&.find { |tmpupload| tmpupload['Key'][0] == object })

          parts = nil
          uploadedSize = 0
          start_partNumber = 1
          if !upload.nil?
            uploadId = upload['UploadId'][0]
            parts = list_parts(bucket, object, endpoint, uploadId)
            if !parts.nil? && !parts.empty?
              if parts[-1]['Size'][0].to_i != 5_242_880
                # the part is the last one, if its size is over 5m, then finish this upload
                complete_multipart_upload(bucket, object, endpoint, uploadId)
                return
              end
              uploadedSize = (parts[0]['Size'][0].to_i * (parts.size - 1)) + parts[-1]['Size'][0].to_i
              start_partNumber = parts[-1]['PartNumber'][0].to_i + 1
            end
          else
            # create upload ID
            uploadId = initiate_multipart_upload(bucket, object, endpoint)
          end

          if file.size <= uploadedSize
            complete_multipart_upload(bucket, object, endpoint, uploadId)
            return
          end

          end_partNumber = (file.size + 5_242_880 - 1) / 5_242_880
          file.seek(uploadedSize)

          for i in start_partNumber..end_partNumber
            body = file.read(5_242_880)
            upload_part(bucket, object, endpoint, i.to_s, uploadId, body)
          end

          complete_multipart_upload(bucket, object, endpoint, uploadId)
        end

        def initiate_multipart_upload(bucket, object, endpoint)
          if endpoint.nil?
            location = get_bucket_location(bucket)
            endpoint = 'http://' + location + '.aliyuncs.com'
          end
          path = object + '?uploads'
          resource = bucket + '/' + path
          ret = request(
            expects: 200,
            method: 'POST',
            path: path,
            bucket: bucket,
            resource: resource,
            endpoint: endpoint
          )
          uploadid = XmlSimple.xml_in(ret.data[:body])['UploadId'][0]
        end

        def upload_part(bucket, object, endpoint, partNumber, uploadId, body)
          if endpoint.nil?
            location = get_bucket_location(bucket)
            endpoint = 'http://' + location + '.aliyuncs.com'
          end
          path = object + '?partNumber=' + partNumber + '&uploadId=' + uploadId
          resource = bucket + '/' + path
          ret = request(
            expects: [200, 203],
            method: 'PUT',
            path: path,
            bucket: bucket,
            resource: resource,
            body: body,
            endpoint: endpoint
          )
        end

        def complete_multipart_upload(bucket, object, endpoint, uploadId)
          if endpoint.nil?
            location = get_bucket_location(bucket)
            endpoint = 'http://' + location + '.aliyuncs.com'
          end
          parts = list_parts(bucket, object, endpoint, uploadId, options = {})
          request_part = []
          return if parts.empty?
          for i in 0..(parts.size - 1)
            part = parts[i]
            request_part[i] = { 'PartNumber' => part['PartNumber'], 'ETag' => part['ETag'] }
          end
          body = XmlSimple.xml_out({ 'Part' => request_part }, 'RootName' => 'CompleteMultipartUpload')

          path = object + '?uploadId=' + uploadId
          resource = bucket + '/' + path
          ret = request(
            expects: 200,
            method: 'POST',
            path: path,
            bucket: bucket,
            resource: resource,
            endpoint: endpoint,
            body: body
          )
        end
      end

      class Mock
        def put_object(object, file = nil, options = {}); end
      end
    end
  end
end