Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Text
Imports Microsoft.Extensions.Logging
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Converters
Imports Newtonsoft.Json.Serialization

Public Class TamiHttpClient
    Implements IDisposable

    Private ReadOnly _client As HttpClient
    Private ReadOnly _logger As ILogger
    Public ReadOnly _secretKey As String

    Public Sub New(baseAddress As String, merchantId As Long, terminalId As Long, secretKey As String, Optional timeoutSecond As Integer = 30, Optional defaultMediaType As String = "application/json", Optional logger As ILogger = Nothing)
        _client = New HttpClient() With {
        .BaseAddress = New Uri(baseAddress)
    }
        _secretKey = secretKey
        _client.DefaultRequestHeaders.Accept.Clear()
        _client.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue(defaultMediaType))

        _client.DefaultRequestHeaders.Add("Accept-Language", "tr")

        ' HttpClient çağrılarına header parametresi olarak PG-Api-Version ve PG-AUTH-TOKEN ekleniyor.
        _client.DefaultRequestHeaders.Add("PG-Api-Version", "v3")
        _client.DefaultRequestHeaders.Add("PG-Auth-Token", $"{merchantId}:{terminalId}:{CryptoHelper.GetPagoHash(merchantId.ToString() & terminalId.ToString() & secretKey)}")

        _client.Timeout = TimeSpan.FromSeconds(timeoutSecond)
        _logger = logger
    End Sub

    Public Async Function GetAsync(Of T)(url As String, Optional correlationId As String = Nothing) As Task(Of T)
        Try
            _client.DefaultRequestHeaders.Remove("correlationId")
            _client.DefaultRequestHeaders.Add("correlationId", If(correlationId, Guid.NewGuid().ToString("N")))

            Dim response As HttpResponseMessage = Await _client.GetAsync(url)
            If response.IsSuccessStatusCode Then
                Dim result = Await response.Content.ReadAsStringAsync()
                Return JsonConvert.DeserializeObject(Of T)(result)
            Else
                Throw New HttpRequestException($"Error: {response.StatusCode}")
            End If
        Catch e As Exception
            _logger?.LogError(e, $"GET request to {url} failed.")
            Throw
        End Try
    End Function

    Public Async Function PostAsync(Of T As ResponseBase)(url As String, data As Object, Optional correlationId As String = Nothing) As Task(Of T)
        Try
            Dim content = AsJson(data)
            _client.DefaultRequestHeaders.Remove("correlationId")
            _client.DefaultRequestHeaders.Add("correlationId", If(correlationId, Guid.NewGuid().ToString("N")))
            Dim response As HttpResponseMessage = Await _client.PostAsync(url, content)

            If response.IsSuccessStatusCode Then
                Dim result = Await response.Content.ReadAsStringAsync()
                Dim returnObj = JsonConvert.DeserializeObject(Of T)(result, New JsonSerializerSettings() With {
                    .ContractResolver = New CamelCasePropertyNamesContractResolver()
                })
                returnObj.RequestRawJson = JsonConvert.SerializeObject(data)
                returnObj.ResponseRawJson = result
                Return returnObj
            Else
                Dim result = Await response.Content.ReadAsStringAsync()
                Throw New HttpRequestException($"Error: {response.StatusCode}, ServiceResult: {result}")
            End If
        Catch e As Exception
            _logger?.LogError(e, $"POST request to {url} with data {data} failed.")
            Throw
        End Try
    End Function

    Private Shared Function AsJson(o As Object) As StringContent
        Return New StringContent(JsonConvert.SerializeObject(o, New JsonSerializerSettings() With {
            .Converters = {New IsoDateTimeConverter With {.DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fff"}},
            .NullValueHandling = NullValueHandling.Ignore,
            .ContractResolver = New CamelCasePropertyNamesContractResolver()
        }), Encoding.Default, "application/json")
    End Function

    Private Sub IDisposable_Dispose() Implements IDisposable.Dispose
        _client?.Dispose()
    End Sub
End Class
