profile = "social-media/publish-post@2.0"
provider = "linkedin"

"""
Publish Share to LinkedIn
For text-only posts, links, and images (up to 200MB per image).
"""
map PublishPost {

  set {
    // FIXME: New posts API doesn't support content scraping: https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/posts-api?view=li-lms-2023-02&tabs=curl#article-post-creation-sample-request
    // AND title is required, so this is a poor attempt to provide at least something
    content = input.link ? {article: { source: input.link, title: input.title || input.link }} : undefined
    media = Array.isArray(input.media) ? input.media : []
    text = input.text
  }

  // FIXME: Workaround for https://github.com/superfaceai/one-sdk-js/issues/215
  images = call foreach(index of Array(media.length).fill().map((_, i) => i)) UploadImage(media = input.media, index = index, accessToken = parameters.accessToken, profileId = input.profileId)

  set if (Array.isArray(images) && images.length > 0) {
    // append the link to the post
    text = input.link ? `${text || ''} ${input.link}`.trim() : text
    content = undefined // FIXME: Avoid SDK's "fun" feature of merging objects implicitly
  }
  set if (Array.isArray(images) && images.length === 1) {
    content = {media: images[0]}
  }
  // multiple images are special case
  // https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/multiimage-post-api?view=li-lms-2023-02&tabs=http#create-multiimage-content
  set if (Array.isArray(images) && images.length > 1) {
    content = {
      multiImage: {
        images: images,
      }
    }
  }
  // https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/posts-api?view=li-lms-2023-02&tabs=http#create-a-post
  http POST "/rest/posts" {
    request "application/json" {
      headers {
        "Authorization" = `Bearer ${parameters.accessToken}`,
        "LinkedIn-Version" = "202302",
        "X-Restli-Protocol-Version" = "2.0.0",
        "Accept" = "application/json",
      }

      body {
        author = input.profileId,
        commentary = text || "",
        visibility = "PUBLIC",
        lifecycleState = "PUBLISHED",
        distribution = {
          feedDistribution: "MAIN_FEED",
          targetEntities: [],
          thirdPartyDistributionChannels: []
        },
        content = content,
      }

    }
  
    response 201 "application/json" {
      urn = headers["x-restli-id"]

      return map result {
        postId = urn,
        url = `https://www.linkedin.com/feed/update/${urn}`,
      }
    }

    response {
      error = call MapError(statusCode = statusCode, body = body)
      map error error
    }
  }
}

// https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/images-api?view=li-lms-2023-02&tabs=http#uploading-an-image
operation UploadImage {
  mediaItem = args.media[args.index]

  set {
    uploadPath = undefined
    assetUrn = undefined
  }
  // 1. register upload
  http POST "/rest/images" {
    security none
    request {
      query {
        action = "initializeUpload",
      }
      headers {
        "X-RestLi-Protocol-Version" = "2.0.0",
        "Authorization" = `Bearer ${args.accessToken}`,
        "LinkedIn-Version" = "202302",
        "Accept" = "application/json",
      }
      body {
        initializeUploadRequest = {
          owner: args.profileId,
        }
      }
    }
  
    response 200 "application/json" {
      uploadPath = body.value.uploadUrl.replace('https://www.linkedin.com/', '') // no URL parsing for you
      assetUrn = body.value.image
    }

    response {
      error = call MapError(statusCode = statusCode, body = body)
      fail error
    }
  }

  // 2. upload the image
  // https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/vector-asset-api?view=li-lms-2023-02&tabs=http#upload-the-image
  http PUT "www" "/{uploadPath}" {
    request "application/octet-stream" {
      headers {
        "Authorization" = `Bearer ${args.accessToken}`,
      }

      body = mediaItem.contents
    }

    response 201 {
      return {
        id = assetUrn,
        altText = mediaItem.altText,
      }
    }

    // Error responses from this endpoint have empty body
    response 400 {
      fail {
        title = "Bad Request",
        detail = "Media upload resulted in Bad Request error."
      }
    }

    response 401 {
      fail {
        title = "Unauthorized",
        detail = "Media upload resulted in Unauthorized error. The access token is either invalid, or expired."
      }
    }
  }
}

// https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/share-api?tabs=http#common-creation-errors
operation MapError {
  statusCode = args.statusCode
  title = `Unknown response: HTTP ${statusCode}`
  body = args.body
  detail = body.message

  set if (statusCode === 400) {
    title = "Bad Request"
  }
  set if (statusCode === 401) {
    title = "Unauthorized"
  }
  set if (statusCode === 403) {
    title = "Forbidden"
  }
  set if (statusCode === 404) {
    title = "Not Found"
  }
  set if (statusCode === 422) {
    title = "Conflict"
  }
  set if (statusCode === 429) {
    title = "Too Many Requests"
  }
  set if (statusCode === 500) {
    title = "Server Error"
  }

  return {
    title = title,
    detail = detail,
    original = body,
  }
}