PSWoolworthsRewards.psm1

function Connect-WoolworthsRewards {
  param (
    [string]$BaseUri = "https://api.woolworthsrewards.com.au/wx",
    # defaultClientId, or wow[|Dev|Local|UAT|PT][Online|Mobile]ClientId in index.html footer script
    [string]$ClientId = "8h41mMOiDULmlLT28xKSv5ITpp3XBRvH"
  )

  $script:Session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
  $script:Session.Headers.client_id = $ClientId

  $script:BaseUri = $BaseUri
  $script:Auth = @{
    bearerExpiry = [DateTime]::MaxValue
    refreshExpiry = [DateTime]::MaxValue
  }

  $script:Creds = @{
    username = Read-Host 'Email or card number'
    password = Read-Host 'Password' -MaskInput
  } | ConvertTo-Json

  New-WRSession
  Write-Host 'Successfully authenticated to Woolworths Rewards'
}

function Disconnect-WoolworthsRewards {
  $body = @{'access_token' = $script:Auth.bearer} | ConvertTo-Json
  Invoke-WRRequest 'security/logout/rewards' 'POST' 'v2' -Body $body -ContentType 'application/json'
}

function New-WRSession {
  param (
    $option = 'basic'
  )
  
  $now = Get-Date # set before API call to avoid time desync with server
  $res = switch ($option) {
    'basic' { Invoke-WRRequest 'security/login/rewards' 'POST' 'v2' -Body $script:Creds -ContentType 'application/json' }
    'otp' {
      # TODO implement OTP initialisation
      Invoke-RestMethod 'https://accounts.woolworthsrewards.com.au/er-login/validate-user' -Method Post -Body @{
        otp = $script:Creds
        action = 'VALIDATE_OTP'
      } 
    }
  }
  Update-WRAuth $now $res
}

function Update-WRAuth {
  param (
    [Parameter(Mandatory = $true)]
    [DateTime]$SessionStart,

    [Parameter(Mandatory = $true)]
    $res

    # [Parameter(Mandatory = $true, ParameterSetName='bearer')]
    # [String]$Bearer,
    # [Parameter(Mandatory = $true, ParameterSetName='bearer')]
    # [String]$bearerExpiredInSeconds,

    # [Parameter(ParameterSetName='refresh')]
    # [String]$refresh,
    # [Parameter(ParameterSetName='refresh')]
    # [String]$refreshExpiredInSeconds
  )

  $script:Session.Headers.Authorization = "Bearer $($res.bearer)"

  $script:Auth.bearer = $res.bearer
  $script:Auth.bearerExpiry = $SessionStart.AddSeconds($res.bearerExpiredInSeconds)

  if ($res.refresh) {
    $script:Auth.refresh = $res.refresh
    $script:Auth.refreshExpiry = $SessionStart.AddSeconds($res.refreshExpiredInSeconds)
  }
}

function Invoke-WRRequest {
  param (
    [String]$path,

    [String]$method = 'GET',
    [String]$version = 'v1',

    [String]$uri = "$($script:BaseUri)/$version/$path"
  )
  
  $now = Get-Date
  if ($now -gt $script:Auth.bearerExpiry) {
    $script:Auth.bearerExpiry = [DateTime]::MaxValue

    if ($now -gt $script:Auth.refreshExpiry) {
      $script:Auth.refreshExpiry = [DateTime]::MaxValue
      New-WRSession
    } else {
      $res = Invoke-WRRequest 'security/refreshLogin' 'POST' 'v2' -Body (@{'refresh_token' = $script:Auth.refresh} | ConvertTo-Json)
      $script:Auth = Update-WRAuth $now $res
    }
  }
  
  "$method $uri", $script:Session.Headers | Out-String | write-host
  $res = Invoke-RestMethod -Uri $uri -Method $method -WebSession $script:Session @args
  return $res.data
}

function Get-WRTransactionPDF {
  param (
    [Parameter(Mandatory = $true)]
    [String]$downloadUrl
  )

  return Invoke-WRRequest 'rewards/member/ereceipts/transactions/details/download' 'POST' -Body (@{'downloadUrl' = $downloadUrl} | ConvertTo-Json)
}

function Get-WRTransactions {
  param (
    [Int]$Page = 1
  )

  Invoke-WRRequest "rewards/member/ereceipts/transactions/list?page=$Page"
}

function Get-WRTransactionDetails {
  param (
    [Parameter(Mandatory = $true)]
    [String]$ReceiptKey
  )

  Invoke-WRRequest 'rewards/member/ereceipts/transactions/details' 'POST' -Body (@{receiptKey = $ReceiptKey} | ConvertTo-Json)
}

function Get-WRProfile {
  $res = Invoke-WRRequest "member/profile"
  return $res.member
}

# Get transactions
# $res = @(1,2) | % { Get-WRTransactions $_ } | % { Get-WRTransactionDetails $_.receiptKey }

# Itemise
# $items = $res.receiptDetails.details | where __typename -eq 'ReceiptDetailsItems' | select -ExpandProperty items

# Set price for quantified items, ignoring sale lines
# for ($i=0; $i -lt $items.count; $i++) {
# if ($items[$i].amount -eq '' -and $items[$i].description -notlike 'PRICE REDUCED*') {
# $items[$i].amount = $items[$i+1].amount
# $items[$i+1].amount = $null
# }
# }

# Filter out quantities and sale lines
# $items | ? amount -notin @('',$null)

# $items | where description -like '*milk*' | measure-object -Sum amount | select Sum

# GET member/profile
# GET member/addresses
# GET member/contacts
# GET member/accounts/rewards/cards?addname=true loyalty card data
# GET member/preferences/security OTP prefs
# GET member/preferences/receipt
# GET member/preferences/redemption
# GET member/preferences/redemption/lock
# GET customerdatareport/request/rewards rewards?
# GET v2/security/hashcrn CRN hashes?
# GET v2/security/social/status?type=Facebook facebook link status
# POST secure/c2login-stepup resendOTP: false
# POST wx/api-otp/verifytoken otptoken
# GET wx/connect/v3/authetoken Apple card JWT?
# {
# "crn": "330000000000#######",
# "edr_card": "#############",
# "wallet_type": "APPLE",
# "jwt_version": "1",
# "created": "DateTime",
# "env": "PROD"
# }
# GET /wx/connect/android/$JWT uses the apple JWT??? returns an android JWT with tons of metadata