. ${PSScriptRoot}\..\logging.ps1

function MapLanguageToRequestParam($language)
{
    $lang = $language
    # Update language name to match those in API cosmos DB. Cosmos SQL is case sensitive and handling this within the query makes it slow.
    if($lang -eq 'javascript'){
        $lang = "JavaScript"
    }
    elseif ($lang -eq "dotnet"){
        $lang = "C%23"
    }
    elseif ($lang -eq "java"){
        $lang = "Java"
    }
    elseif ($lang -eq "python"){
        $lang = "Python"
    }
    else{
      $lang = $null
    }
    return $lang
}

function Check-ApiReviewStatus($packageName, $packageVersion, $language, $url, $apiKey, $apiApprovalStatus = $null, $packageNameStatus = $null)
{
  # Get API view URL and API Key to check status
  Write-Host "Checking API review status for package: ${packageName}"
  $lang = MapLanguageToRequestParam -language $language
  if ($lang -eq $null) {
    return
  }
  $headers = @{ "ApiKey" = $apiKey }

  if (!$apiApprovalStatus) {
    $apiApprovalStatus = [PSCustomObject]@{
      IsApproved = $false
      Details = ""
    }
  }

  if (!$packageNameStatus) {
    $packageNameStatus = [PSCustomObject]@{
      IsApproved = $false
      Details = ""
    }
  }

  try
  {
    $requestUrl = "${url}?language=${lang}&packageName=${packageName}&packageVersion=${packageVersion}"
    Write-Host "Request to APIView: [${requestUrl}]"
    $response = Invoke-WebRequest $requestUrl -Method 'GET' -Headers $headers
    Write-Host "Response: $($response.StatusCode)"
    Process-ReviewStatusCode -statusCode $response.StatusCode -packageName $packageName -apiApprovalStatus $apiApprovalStatus -packageNameStatus $packageNameStatus
    if ($apiApprovalStatus.IsApproved) {
      Write-Host $($apiApprovalStatus.Details)
    }
    else {
      Write-warning $($apiApprovalStatus.Details)
    }
    if ($packageNameStatus.IsApproved) {
      Write-Host $($packageNameStatus.Details)
    }
    else {
      Write-warning $($packageNameStatus.Details)
    }
  }
  catch
  {
    Write-Warning "Failed to check API review status for package $($PackageName). You can check http://aka.ms/azsdk/engsys/apireview/faq for more details on API Approval."
  }
}

function Process-ReviewStatusCode($statusCode, $packageName, $apiApprovalStatus, $packageNameStatus)
{
  $apiApproved = $false
  $apiApprovalDetails = "API Review is not approved for package $($packageName). Release pipeline will fail if API review is not approved for a GA version release. You can check http://aka.ms/azsdk/engsys/apireview/faq for more details on API Approval."
  $apiApprovalDetails += " Once your API is approved, re-trigger the release pipeline again."

  $packageNameApproved = $false
  $packageNameApprovalDetails = ""

  # 200 API approved and Package name approved
  # 201 API review is not approved, Package name is approved
  # 202 API review is not approved, Package name is not approved

  switch ($statusCode)
  {
    200
    {
      $apiApprovalDetails = "API Review is approved for package $($packageName)"
      $apiApproved = $true

      $packageNameApproved = $true
      $packageNameApprovalDetails = "Package name is approved for package $($packageName)"
    }
    201
    {
      $packageNameApproved = $true
      $packageNameApprovalDetails = "Package name is approved for package $($packageName)"
    }
    202
    {
      $packageNameApprovalDetails = "Package name $($packageName) is not yet approved by an SDK API approver. Package name must be approved to release a beta version if $($packageName) was never released as a stable version."
      $packageNameApprovalDetails += " You can check http://aka.ms/azsdk/engsys/apireview/faq for more details on package name Approval."
    }
    default
    {
      $apiApprovalDetails = "Invalid status code from APIView. status code $($statusCode)"
      $packageNameApprovalDetails = "Invalid status code from APIView. status code $($statusCode)"
      Write-Error "Failed to process API Review status for for package $($PackageName). Please reach out to Azure SDK engineering systems on teams channel."
    }
  }

  $apiApprovalStatus.IsApproved = $apiApproved
  $apiApprovalStatus.Details = $apiApprovalDetails

  $packageNameStatus.IsApproved = $packageNameApproved
  $packageNameStatus.Details = $packageNameApprovalDetails
}

function Set-ApiViewCommentForRelatedIssues {
  param (
    [Parameter(Mandatory = $true)]
    [string]$HeadCommitish,
    [string]$APIViewHost = "https://apiview.dev",
    [ValidateNotNullOrEmpty()]
    [Parameter(Mandatory = $true)]
    $AuthToken,
    [string]$GitHubActionRunUrl = ""
  )
  . ${PSScriptRoot}\..\common.ps1
  $issuesForCommit = $null
  try {
    $issuesForCommit = Search-GitHubIssues -CommitHash $HeadCommitish -AuthToken $AuthToken
    if ($issuesForCommit.items.Count -eq 0) {
      LogInfo "No issues found for commit: $HeadCommitish"
      Write-Host "##vso[task.complete result=SucceededWithIssues;]DONE"
      exit 0
    }
  } catch {
    LogError "No issues found for commit: $HeadCommitish"
    exit 1
  }
  $issuesForCommit.items | ForEach-Object {
    $urlParts = $_.url -split "/"
    Set-ApiViewCommentForPR -RepoOwner $urlParts[4] -RepoName $urlParts[5] -PrNumber $urlParts[7] -HeadCommitish $HeadCommitish -APIViewHost $APIViewHost -AuthToken $AuthToken -GitHubActionRunUrl $GitHubActionRunUrl
  }
}

function Set-ApiViewCommentForPR {
  param (
    [Parameter(Mandatory = $true)]
    [string]$RepoOwner,
    [Parameter(Mandatory = $true)]
    [string]$RepoName,
    [Parameter(Mandatory = $true)]
    [string]$PrNumber,
    [Parameter(Mandatory = $true)]
    [string]$HeadCommitish,
    [Parameter(Mandatory = $true)]
    [string]$APIViewHost,
    [ValidateNotNullOrEmpty()]
    [Parameter(Mandatory = $true)]
    $AuthToken,
    [string]$GitHubActionRunUrl = ""
  )
  $repoFullName = "$RepoOwner/$RepoName"
  $apiviewEndpoint = "$APIViewHost/api/pullrequests?pullRequestNumber=$PrNumber&repoName=$repoFullName&commitSHA=$HeadCommitish"
  LogDebug "Get APIView information for PR using endpoint: $apiviewEndpoint"

  $correlationId = [System.Guid]::NewGuid().ToString()
  $headers = @{
    "x-correlation-id" = $correlationId
  }
  LogInfo "Correlation ID: $correlationId"

  $commentText = @()
  $commentText += "## API Change Check"
  try {
    $response = Invoke-WebRequest -Uri $apiviewEndpoint -Method Get -Headers $headers -MaximumRetryCount 3
    LogInfo "OperationId: $($response.Headers['X-Operation-Id'])"
    if ($response.StatusCode -ne 200) {
      LogInfo "API changes are not detected in this pull request."
      exit 0
    }
    else {
      LogSuccess "APIView identified API level changes in this PR and created $($response.Count) API reviews"
      $commentText += ""
      $commentText += "APIView identified API level changes in this PR and created the following API reviews"
      $commentText += ""

      $responseContent = $response.Content | ConvertFrom-Json
      if ($RepoName.StartsWith(("azure-sdk-for-"))) {
        $responseContent | ForEach-Object {
          $commentText += "[$($_.packageName)]($($_.url))"
        }
      } else {
        $commentText += "| Language | API Review for Package |"
        $commentText += "|----------|---------|"
        $responseContent | ForEach-Object {
          $commentText += "| $($_.language) | [$($_.packageName)]($($_.url)) |"
        }
      }
    }
  } catch{
    LogError "Failed to get API View information for PR: $PrNumber in repo: $repoFullName with commitSHA: $HeadCommitish. Error: $_"
    exit 1
  }

  $commentText += "<!-- Fetch URI: $apiviewEndpoint -->"
  if (-not [string]::IsNullOrWhiteSpace($GitHubActionRunUrl)) {
    $commentText += ""
    $commentText += "---"
    $commentText += "Comment generated by [After APIView]($GitHubActionRunUrl) workflow run."
  }
  $commentText = $commentText -join "`r`n"
  $existingComment = $null;
  $existingAPIViewComment = $null;

  try {
    $existingComment = Get-GitHubIssueComments -RepoOwner $RepoOwner -RepoName $RepoName -IssueNumber $PrNumber -AuthToken $AuthToken
    $existingAPIViewComment = $existingComment | Where-Object {
      $_.body.StartsWith("**API Change Check**", [StringComparison]::OrdinalIgnoreCase) -or $_.body.StartsWith("## API Change Check", [StringComparison]::OrdinalIgnoreCase) } | Select-Object -Last 1
  } catch {
    LogWarning "Failed to get comments from Pull Request: $PrNumber in repo: $repoFullName"
  }

  try {
    if ($existingAPIViewComment) {
      LogDebug "Updating existing APIView comment..."
      Update-GitHubIssueComment -RepoOwner $RepoOwner -RepoName $RepoName `
                                -CommentId $existingAPIViewComment.id -Comment $commentText `
                                -AuthToken $AuthToken
    } else {
      LogDebug "Creating new APIView comment..."
      Add-GitHubIssueComment -RepoOwner $RepoOwner -RepoName $RepoName `
                             -IssueNumber $PrNumber -Comment $commentText `
                             -AuthToken $AuthToken
    }
  } catch {
    LogError "Failed to set PR comment for APIView. Error: $_"
    exit 1
  }
}

# Helper function used to create API review requests for Spec generation SDKs pipelines
function Create-API-Review {
  param (
    [string]$apiviewEndpoint = "https://apiview.dev/api/PullRequests/CreateAPIRevisionIfAPIHasChanges",
    [string]$specGenSDKArtifactPath,
    [string]$apiviewArtifactName,
    [string]$buildId,
    [string]$commitish,
    [string]$repoName,
    [string]$pullRequestNumber
  )
  $specGenSDKContent = Get-Content -Path $SpecGenSDKArtifactPath -Raw | ConvertFrom-Json
  $language = ($specGenSDKContent.language -split "-")[-1]

  foreach ($requestData in $specGenSDKContent.apiViewRequestData) {
    $requestUri = [System.UriBuilder]$apiviewEndpoint
    $requestParam = [System.Web.HttpUtility]::ParseQueryString('')
    $requestParam.Add('artifactName', $apiviewArtifactName)
    $requestParam.Add('buildId', $buildId)
    $requestParam.Add('commitSha', $commitish)
    $requestParam.Add('repoName', $repoName)
    $requestParam.Add('pullRequestNumber', $pullRequestNumber)
    $requestParam.Add('packageName', $requestData.packageName)
    $requestParam.Add('filePath', $requestData.filePath)
    if ($language -ieq "python") {
      $requestParam.Add('codeFile', (Split-Path -Path $requestData.filePath -Leaf))
    }
    $requestParam.Add('language', $language)
    $requestUri.query = $requestParam.toString()
    $correlationId = [System.Guid]::NewGuid().ToString()

    $headers = @{
      "x-correlation-id" = $correlationId
    }

    LogInfo "Request URI: $($requestUri.Uri.OriginalString)"
    LogInfo "Correlation ID: $correlationId"

    try
    {
      $response = Invoke-WebRequest -Method 'GET' -Uri $requestUri.Uri -Headers $headers -MaximumRetryCount 3
      if ($response.StatusCode -eq 201 -or $response.StatusCode -eq 208) {
        if ($response.StatusCode -eq 201) {
          LogSuccess "Status Code: $($response.StatusCode)`nAPI review request created successfully"
        }
        elseif ($response.StatusCode -eq 208) {
          LogSuccess "Status Code: $($response.StatusCode)`nThere is no API change compared with the previous version."
        }
        if ($response.Headers['Content-Type'] -like 'application/json*') {
          $responseContent = $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 10
          LogSuccess "Response:`n$($responseContent)"
        }
        else {
          LogSuccess "Response: $($response.Content)"
        }
      }
      else {
        LogError "Failed to create API review request. $($response)"
        exit 1
      }
    }
    catch
    {
      LogError "Error : $($_.Exception)"
      exit 1
    }
  }
}