$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Net.Http

$baseUrl = 'http://localhost:5099'
$stamp = Get-Date -Format 'yyyyMMddHHmmss'
$username = "smoke$stamp"
$password = 'Demo123!'
$phoneNumber = "+7900$stamp"

$session = Invoke-RestMethod `
    -Uri "$baseUrl/api/auth/register" `
    -Method Post `
    -ContentType 'application/json' `
    -Body (@{
        username = $username
        displayName = 'Smoke Test'
        password = $password
        phoneNumber = $phoneNumber
    } | ConvertTo-Json)

$refreshed = Invoke-RestMethod `
    -Uri "$baseUrl/api/auth/refresh" `
    -Method Post `
    -ContentType 'application/json' `
    -Body (@{
        refreshToken = $session.refreshToken
    } | ConvertTo-Json)

$headers = @{
    Authorization = "Bearer $($refreshed.accessToken)"
}

$installationId = "push-$stamp"
$pushDevice = Invoke-RestMethod `
    -Uri "$baseUrl/api/push/devices" `
    -Headers $headers `
    -Method Post `
    -ContentType 'application/json' `
    -Body (@{
        platform = 1
        installationId = $installationId
        deviceToken = "token-$stamp"
        deviceName = "Smoke Android $stamp"
        notificationsEnabled = $true
    } | ConvertTo-Json)

$pushDevicesAfterRegister = Invoke-RestMethod `
    -Uri "$baseUrl/api/push/devices" `
    -Headers $headers `
    -Method Get
if ($null -eq $pushDevicesAfterRegister) {
    $pushDevicesAfterRegister = @()
}
else {
    $pushDevicesAfterRegister = @($pushDevicesAfterRegister)
}

$bob = Invoke-RestMethod -Uri "$baseUrl/api/users/search?q=bob" -Headers $headers -Method Get | Select-Object -First 1
$channel = Invoke-RestMethod `
    -Uri "$baseUrl/api/chats/channel" `
    -Headers $headers `
    -Method Post `
    -ContentType 'application/json' `
    -Body (@{
        title = "Smoke Channel $stamp"
        memberIds = @($bob.id)
    } | ConvertTo-Json)

$message = Invoke-RestMethod `
    -Uri "$baseUrl/api/chats/$($channel.id)/messages" `
    -Headers $headers `
    -Method Post `
    -ContentType 'application/json' `
    -Body (@{ text = 'channel smoke message' } | ConvertTo-Json)

$edited = Invoke-RestMethod `
    -Uri "$baseUrl/api/chats/$($channel.id)/messages/$($message.id)" `
    -Headers $headers `
    -Method Put `
    -ContentType 'application/json' `
    -Body (@{ text = 'channel smoke message edited' } | ConvertTo-Json)

$tempFile = Join-Path $env:TEMP "ikar-voice-$stamp.ogg"
"voice smoke test $stamp" | Set-Content -Path $tempFile -NoNewline

$handler = New-Object System.Net.Http.HttpClientHandler
$client = New-Object System.Net.Http.HttpClient($handler)
$client.BaseAddress = [Uri]::new("$baseUrl/")
$client.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue('Bearer', $refreshed.accessToken)
$form = New-Object System.Net.Http.MultipartFormDataContent
$form.Add((New-Object System.Net.Http.StringContent('voice caption')), 'text')
$fileStream = [System.IO.File]::OpenRead($tempFile)
$fileContent = New-Object System.Net.Http.StreamContent($fileStream)
$fileContent.Headers.ContentType = New-Object System.Net.Http.Headers.MediaTypeHeaderValue('audio/ogg')
$form.Add($fileContent, 'file', [System.IO.Path]::GetFileName($tempFile))
$uploadResponse = $client.PostAsync("api/chats/$($channel.id)/attachments", $form).GetAwaiter().GetResult()
$uploadPayload = $uploadResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult()
$fileStream.Dispose()
$form.Dispose()
$client.Dispose()
$handler.Dispose()

if (-not $uploadResponse.IsSuccessStatusCode) {
    throw $uploadPayload
}

$voiceMessage = $uploadPayload | ConvertFrom-Json
$attachment = $voiceMessage.attachments[0]
$downloadHandler = New-Object System.Net.Http.HttpClientHandler
$downloadClient = New-Object System.Net.Http.HttpClient($downloadHandler)
$downloadClient.BaseAddress = [Uri]::new("$baseUrl/")
$downloadClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue('Bearer', $refreshed.accessToken)
$downloadResponse = $downloadClient.GetAsync($attachment.downloadPath.TrimStart('/')).GetAwaiter().GetResult()
$downloadBytes = $downloadResponse.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult()
$downloadClient.Dispose()
$downloadHandler.Dispose()

$deleted = Invoke-RestMethod `
    -Uri "$baseUrl/api/chats/$($channel.id)/messages/$($message.id)" `
    -Headers $headers `
    -Method Delete

$bobSession = Invoke-RestMethod `
    -Uri "$baseUrl/api/auth/login" `
    -Method Post `
    -ContentType 'application/json' `
    -Body (@{
        username = 'bob'
        password = 'demo123'
    } | ConvertTo-Json)

$bobHeaders = @{
    Authorization = "Bearer $($bobSession.accessToken)"
}

$subscriberBlocked = $false
try {
    Invoke-RestMethod `
        -Uri "$baseUrl/api/chats/$($channel.id)/messages" `
        -Headers $bobHeaders `
        -Method Post `
        -ContentType 'application/json' `
        -Body (@{ text = 'subscriber should not publish' } | ConvertTo-Json) | Out-Null
}
catch {
    $subscriberBlocked = $_.Exception.Response.StatusCode.value__ -eq 403
}

$channelForBob = Invoke-RestMethod -Uri "$baseUrl/api/chats/$($channel.id)" -Headers $bobHeaders -Method Get
$channelReload = Invoke-RestMethod -Uri "$baseUrl/api/chats/$($channel.id)" -Headers $headers -Method Get
Invoke-RestMethod -Uri "$baseUrl/api/push/devices/$installationId" -Headers $headers -Method Delete | Out-Null
$pushDevicesAfterDelete = Invoke-RestMethod `
    -Uri "$baseUrl/api/push/devices" `
    -Headers $headers `
    -Method Get
if ($null -eq $pushDevicesAfterDelete) {
    $pushDevicesAfterDelete = @()
}
else {
    $pushDevicesAfterDelete = @($pushDevicesAfterDelete)
}
Invoke-RestMethod -Uri "$baseUrl/api/auth/logout" -Headers $headers -Method Post | Out-Null

$logoutBlocked = $false
try {
    Invoke-RestMethod -Uri "$baseUrl/api/users/me" -Headers $headers -Method Get | Out-Null
}
catch {
    $logoutBlocked = $_.Exception.Response.StatusCode.value__ -eq 401
}

[pscustomobject]@{
    Username          = $username
    ChannelId         = $channel.id
    EditedMessageId   = $edited.id
    EditedText        = $edited.text
    DeletedFlag       = $null -ne $deleted.deletedAt
    VoiceAttachmentId = $attachment.id
    VoiceKind         = $attachment.kind
    VoiceKindOk       = ($attachment.kind -eq 2) -or ($attachment.kind -eq 'VoiceNote')
    DownloadOk        = [int]$downloadResponse.StatusCode -eq 200 -and $downloadBytes.Length -eq $attachment.fileSizeBytes
    PushRegistered    = $pushDevice.installationId -eq $installationId
    PushListed        = $pushDevicesAfterRegister.Count -eq 1 -and $pushDevicesAfterRegister[0].installationId -eq $installationId
    PushDeleted       = $pushDevicesAfterDelete.Count -eq 0
    MessageCount      = $channelReload.messages.Count
    SubscriberBlocked = $subscriberBlocked
    ChannelReadOnly   = -not $channelForBob.canSendMessages
    RefreshRotated    = $session.refreshToken -ne $refreshed.refreshToken
    LogoutBlocked     = $logoutBlocked
}
