' ============================================================================
'  GreenPulse – CBAM Carbon Cost API
'  Single-file VB.NET implementation using Chilkat
'  Adapted to Postman Collection 29.12.2025
' ============================================================================

Imports Chilkat
Imports System.Globalization

' ============================================================================
'  MAIN API CLASS
' ============================================================================
Public Class cATEZ_Greenpulse_CBAM_CostCalculation

    Private ReadOnly _baseUrl As String = "https://test-greenpulse-api.singlewindow.io/api/v1-0/public"
    'Private ReadOnly _baseUrl As String = "https://greenpulse-api.singlewindow.io/api/v1-0/public"
    Private ReadOnly _http As Http

    Public Sub New()
        _http = New Http()
        _http.SetRequestHeader("Content-Type", "application/json")
    End Sub

    ' ------------------------------------------------------------------------
    ' GET /carbon-cost/cn-codes
    ' ------------------------------------------------------------------------
    Public Function GetCnCodes() As cCBAM_CnCode_Response

        Dim resp = _http.QuickGetObj(_baseUrl & "/carbon-cost/cn-codes")
        If resp Is Nothing Then Throw New Exception(_http.LastErrorText)

        Dim json As New JsonObject()
        json.Load(resp.BodyStr)

        Dim r As New cCBAM_CnCode_Response With {.success = json.BoolOf("success")}
        If r.success Then
            r.data = New List(Of cCBAM_CnCode)
            Dim a = json.ArrayOf("data")
            For i = 0 To a.Size - 1
                Dim o = a.ObjectAt(i)
                r.data.Add(New cCBAM_CnCode With {
                    .cn_code = o.StringOf("cn_code"),
                    .cn_description = o.StringOf("cn_description")
                })
            Next
        Else
            r.error = ParseError(json)
        End If

        Return r
    End Function


    Public Function insertinto_Greendeal_CBAM_Trn(cCBAM_CnCode_Response As cCBAM_CnCode_Response) As Boolean

        VERAG_PROG_ALLGEMEIN.cGreendeal_CBAM_Trn.DELETE_ALL()

        For Each CN In cCBAM_CnCode_Response.data
            Dim CBAM As New VERAG_PROG_ALLGEMEIN.cGreendeal_CBAM_Trn


            CBAM.trnPattern = CN.cn_code & "%"
            CBAM.sektor = ""
            CBAM.ware = CN.cn_description
            CBAM.is_exclusion = False
            CBAM.is_active = True
            CBAM.start_date = "01.10.2023"
            CBAM.end_date = Nothing
            CBAM.SAVE()
        Next
        Return True
    End Function

    ' ------------------------------------------------------------------------
    ' GET /carbon-cost/cn-code-defaults
    ' ------------------------------------------------------------------------
    Public Function GetCnCodeDefaults(
        cn_code As String,
        country_code As String,
        Optional year As Integer? = Nothing
    ) As cCBAM_CnCodeDefaults_Response

        Dim url = $"{_baseUrl}/carbon-cost/cn-code-defaults?cn_code={cn_code}&country_code={country_code}"
        If year.HasValue Then url &= $"&year={year.Value}"

        Console.WriteLine($"[GetCnCodeDefaults] Request URL: {url}")

        Dim resp = _http.QuickGetObj(url)
        If resp Is Nothing Then
            Console.WriteLine("[GetCnCodeDefaults] HTTP request failed: " & _http.LastErrorText)
            Throw New Exception(_http.LastErrorText)
        End If

        Console.WriteLine("[GetCnCodeDefaults] HTTP response received.")
        If resp.BodyStr IsNot Nothing Then
            Console.WriteLine("[GetCnCodeDefaults] Response body length: " & resp.BodyStr.Length)
            Console.WriteLine("[GetCnCodeDefaults] Body: " & resp.BodyStr)
        Else
            Console.WriteLine("[GetCnCodeDefaults] Response body is empty.")
        End If

        Dim json As New JsonObject()
        json.Load(resp.BodyStr)

        Dim successFlag = json.BoolOf("success")
        Console.WriteLine($"[GetCnCodeDefaults] JSON 'success' flag: {successFlag}")

        Dim r As New cCBAM_CnCodeDefaults_Response With {.success = successFlag}

        If r.success Then
            Dim d = json.ObjectOf("data")
            r.data = New cCBAM_CnCodeDefaults With {
                .default_emission = GetDec(d, "default_emission"),
                .benchmark = GetNullableDec(d, "benchmark")
            }
            Console.WriteLine($"[GetCnCodeDefaults] Parsed defaults: default_emission={r.data.default_emission}, benchmark={(If(r.data.benchmark.HasValue, r.data.benchmark.Value.ToString(), "null"))}")
        Else
            r.error = ParseError(json)
            Console.WriteLine($"[GetCnCodeDefaults] API error: code={r.error.code}, message={r.error.message}, statusCode={r.error.statusCode}")
        End If

        Return r
    End Function

    ' ------------------------------------------------------------------------
    ' POST /carbon-cost/calculate
    ' ------------------------------------------------------------------------
    Public Function CalculateCost(req As cCBAM_CostCalculation_Request) _
        As cCBAM_CostCalculation_Response

        VERAG_PROG_ALLGEMEIN.cChilkat_Helper.UnlockCilkat()

        Dim resp = _http.PostJson2(
            _baseUrl & "/carbon-cost/calculate",
            "application/json",
            req.ToJson()
        )

        If resp Is Nothing Then Throw New Exception(_http.LastErrorText)
        Return ParseCalcResponse(resp.BodyStr)
    End Function

    ' ------------------------------------------------------------------------
    ' calcCBAM – Convenience Wrapper
    ' ------------------------------------------------------------------------
    ' ------------------------------------------------------------------------
    ' calcCBAM – mit automatischem Fallback auf CN-Code Defaults
    ' ------------------------------------------------------------------------
    Public Shared Function calcCBAM(cn_code As String, weight As Object, country_code As String, Optional ByRef see_total As Object = Nothing, Optional year As Object = Nothing, Optional ByRef benchmark_value As Object = Nothing, Optional ByRef CBAM_COST As Decimal = -1, Optional ByRef CBAM_EMISSION As Decimal = -1, Optional ByRef CBAM_BENCHMARK As Decimal = -1, Optional ByRef CBAM_FAKTOR As Decimal = -1) As String

        ' ------------------------------------------------------------
        ' Basis-Validierung
        ' ------------------------------------------------------------
        If cn_code Is Nothing OrElse Not IsNumeric(cn_code) _
       OrElse Not {4, 6, 8}.Contains(cn_code.Length) Then
            Return "Fehler: CN-Code muss 4-, 6- oder 8-stellig numerisch sein"
        End If

        If weight Is Nothing OrElse Not IsNumeric(weight) OrElse CDbl(weight) <= 0 Then
            Return "Fehler: Gewicht muss > 0 sein"
        End If

        If country_code Is Nothing OrElse country_code.Length <> 2 Then
            Return "Fehler: country_code muss ISO-2 sein"
        End If

        If year IsNot Nothing AndAlso Not IsNumeric(year) Then
            Return "Fehler: ungültiges Jahr"
        End If

        ' ------------------------------------------------------------
        ' API initialisieren
        ' ------------------------------------------------------------
        Dim api As New cATEZ_Greenpulse_CBAM_CostCalculation()

        ' ------------------------------------------------------------
        ' Prüfen, ob Defaults benötigt werden
        ' ------------------------------------------------------------
        Dim needDefaults As Boolean = False

        If see_total Is Nothing OrElse Not IsNumeric(see_total) OrElse CDbl(see_total) < 0 Then
            needDefaults = True
        End If

        If benchmark_value Is Nothing OrElse Not IsNumeric(benchmark_value) OrElse CDbl(benchmark_value) < 0 Then
            needDefaults = True
        End If

        Dim defaultEmission As Decimal? = Nothing
        Dim defaultBenchmark As Decimal? = Nothing

        If needDefaults Then
            Try
                Dim defResp = api.GetCnCodeDefaults(
                cn_code,
                country_code.ToUpperInvariant(),
                If(IsNumeric(year), CInt(year), CType(Nothing, Integer?))
            )

                If defResp.success Then
                    defaultEmission = defResp.data.default_emission
                    defaultBenchmark = defResp.data.benchmark

                    benchmark_value = defaultBenchmark
                    see_total = defaultEmission
                Else
                    Return $"FEHLER DEFAULTS {defResp.error.code}: {defResp.error.message}"
                End If

            Catch ex As Exception
                Return "Technischer Fehler beim Laden der CN-Code Defaults: " & ex.Message
            End Try
        End If

        ' ------------------------------------------------------------
        ' Request aufbauen
        ' ------------------------------------------------------------
        Dim req As New cCBAM_CostCalculation_Request With {
        .cn_code = cn_code,
        .country_code = country_code.ToUpperInvariant(),
        .weight = CDec(weight)
    }

        ' see_total
        If IsNumeric(see_total) AndAlso CDbl(see_total) >= 0 Then
            req.see_total = CDec(see_total)
        ElseIf defaultEmission.HasValue Then
            req.see_total = defaultEmission.Value
        End If

        ' year
        If IsNumeric(year) Then
            req.year = CInt(year)
        End If

        ' benchmark_value
        If IsNumeric(benchmark_value) AndAlso CDbl(benchmark_value) >= 0 Then
            req.benchmark_value = CDec(benchmark_value)
        ElseIf defaultBenchmark.HasValue Then
            req.benchmark_value = defaultBenchmark.Value
        End If

        ' ------------------------------------------------------------
        ' Calculate
        ' ------------------------------------------------------------
        Dim resp As cCBAM_CostCalculation_Response

        Try
            resp = api.CalculateCost(req)
        Catch ex As Exception
            Return "Technischer Fehler bei Kostenberechnung: " & ex.Message
        End Try

        If Not resp.success Then
            Return $"FEHLER {resp.error.code}: {resp.error.message}"
        End If

        ' ------------------------------------------------------------
        ' Ergebnis formatieren
        ' ------------------------------------------------------------
        Dim d = resp.data
        Dim s As String = ""

        s &= "CBAM Kostenberechnung" & vbCrLf
        s &= "----------------------" & vbCrLf
        s &= $"CN-Code: {cn_code}" & vbCrLf
        s &= $"Ursprungsland: {req.country_code}" & vbCrLf
        s &= $"Gewicht: {req.weight:N2} t" & vbCrLf
        s &= vbCrLf

        's &= $"Kosten: {d.cost:N2} {d.currency}" & vbCrLf
        s &= $"CBAM Emission: {d.cbam_emission:N5}" & vbCrLf
        s &= $"Benchmark: {d.benchmark:N5}" & vbCrLf
        s &= $"Phase-Faktor: {d.phase_factor:P2}" & vbCrLf
        s &= $"CO₂ Preis: {d.carbon_price:N2} EUR/t" & vbCrLf

        If Not String.IsNullOrWhiteSpace(d.info_message) Then
            s &= vbCrLf & "Info: " & d.info_message & vbCrLf
        End If
        s &= vbCrLf
        s &= $"CBAM Kosten: {d.cost:N2} {d.currency}" & vbCrLf
        CBAM_COST = d.cost
        CBAM_EMISSION = see_total
        CBAM_BENCHMARK = benchmark_value
        CBAM_FAKTOR = d.phase_factor
        Return s

    End Function

    Public Shared Function calcCBAM_ByCertificatePrice(cn_code As String, weight As Object, country_code As String, certificate_price As Object, year As Object, Optional ByRef CBAM_COST As Decimal = -1, Optional ByRef CBAM_EMISSION As Decimal = -1, Optional ByRef CBAM_BENCHMARK As Decimal = -1, Optional ByRef CBAM_Faktor As Decimal = -1) As String

        VERAG_PROG_ALLGEMEIN.cChilkat_Helper.UnlockCilkat()

        ' ------------------------------------------------------------
        ' Validierung
        ' ------------------------------------------------------------
        If cn_code Is Nothing OrElse Not IsNumeric(cn_code) _
        OrElse Not {4, 6, 8}.Contains(cn_code.Length) Then
            Return "Fehler: CN-Code muss 4-, 6- oder 8-stellig numerisch sein"
        End If

        If weight Is Nothing OrElse Not IsNumeric(weight) OrElse CDbl(weight) <= 0 Then
            Return "Fehler: Gewicht muss > 0 sein"
        End If

        If country_code Is Nothing OrElse country_code.Length <> 2 Then
            Return "Fehler: country_code muss ISO-2 sein"
        End If

        If certificate_price Is Nothing OrElse Not IsNumeric(certificate_price) OrElse CDbl(certificate_price) <= 0 Then
            Return "Fehler: Zertifikatspreis muss > 0 sein"
        End If

        If year Is Nothing Or Not IsNumeric(year) Then
            Return "Fehler: ungültiges Jahr"
        End If

        ' ------------------------------------------------------------
        ' SEE total (Default-Emission) laden
        ' ------------------------------------------------------------
        Dim api As New cATEZ_Greenpulse_CBAM_CostCalculation()
        Dim see_total As Decimal
        Dim benchmark As Decimal

        Try
            Dim defResp = api.GetCnCodeDefaults(
                cn_code,
                country_code.ToUpperInvariant(),
                  CInt(year)
            )

            If Not defResp.success Then
                Return $"FEHLER DEFAULTS {defResp.error.code}: {defResp.error.message}"
            End If

            If defResp.data.default_emission < 0 Then
                Return "Fehler: Keine Default-Emission (see_total) verfügbar"
            End If

            see_total = defResp.data.default_emission
            benchmark = defResp.data.benchmark

        Catch ex As Exception
            Return "Technischer Fehler beim Laden der CN-Code Defaults: " & ex.Message
        End Try

        ' ------------------------------------------------------------
        ' Berechnung (ohne Phase-Faktor!)
        ' ------------------------------------------------------------
        Dim w As Decimal = CDec(weight)
        Dim price As Decimal = CDec(certificate_price)


        ' --- Faktoren als echte Faktoren (nicht Prozent) ---
        Dim cbamf As Decimal = 0D
        Select Case CInt(year)
            Case 2026 : cbamf = 0.975D
            Case 2027 : cbamf = 0.95D
            Case 2028 : cbamf = 0.9D
            Case 2029 : cbamf = 0.775D
            Case 2030 : cbamf = 0.515D
            Case 2031 : cbamf = 0.39D
            Case 2032 : cbamf = 0.265D
            Case 2033 : cbamf = 0.14D
            Case Else : cbamf = 0D
        End Select


        ' SEFA = Benchmark * CBAMF * CSCF
        Dim sefa As Decimal = benchmark * cbamf

        ' Zertifikatspflicht je Tonne Ware
        Dim certPerTon As Decimal = see_total - sefa
        If certPerTon < 0D Then certPerTon = 0D

        ' Gesamte Zertifikatspflicht (t CO2e)
        Dim certTotal As Decimal = certPerTon * w

        ' Kosten
        Dim cbamCost As Decimal = certTotal * price

        ' --- Ausgabe ---
        Dim s As String = ""
        s &= "CBAM Kostenberechnung (Default EE mit SEFA-Abzug)" & vbCrLf
        s &= "-----------------------------------------------------------" & vbCrLf
        s &= $"CN-Code: {cn_code}" & vbCrLf
        s &= $"Ursprungsland: {country_code.ToUpperInvariant()}" & vbCrLf
        s &= $"Jahr: {year}" & vbCrLf
        s &= $"Gewicht: {w:N2} t" & vbCrLf
        s &= $"EE / SEE total (Default): {see_total:N6} t CO₂e/t" & vbCrLf
        s &= $"Benchmark BMg: {benchmark:N6} t CO₂e/t" & vbCrLf
        s &= $"CBAMF: {cbamf:N3}"
        Console.WriteLine(s)

        CBAM_COST = cbamCost
        CBAM_EMISSION = see_total
        CBAM_BENCHMARK = benchmark
        CBAM_Faktor = cbamf

        Return s

    End Function

    ' ------------------------------------------------------------------------
    ' Helpers
    ' ------------------------------------------------------------------------
    Private Function ParseCalcResponse(jsonStr As String) _
        As cCBAM_CostCalculation_Response

        Dim json As New JsonObject()
        json.Load(jsonStr)

        Dim r As New cCBAM_CostCalculation_Response With {.success = json.BoolOf("success")}

        If r.success Then
            Dim d = json.ObjectOf("data")
            r.data = New cCBAM_CostCalculation_Data With {
                .cost = GetDec(d, "cost"),
                .cbam_emission = GetDec(d, "cbam_emission"),
                .benchmark = GetDec(d, "benchmark"),
                .phase_factor = GetDec(d, "phase_factor"),
                .carbon_price = GetDec(d, "carbon_price"),
                .currency = d.StringOf("currency"),
                .info_message = d.StringOf("info_message")
            }

            Dim cd = d.ObjectOf("calculation_details")
            r.data.calculation_details = New cCBAM_CostCalculation_Details With {
                .cbam_emission = GetDec(cd, "cbam_emission"),
                .benchmark = GetDec(cd, "benchmark"),
                .adjusted_benchmark = GetDec(cd, "adjusted_benchmark"),
                .emission_difference = GetDec(cd, "emission_difference"),
                .weight = GetDec(cd, "weight"),
                .carbon_price = GetDec(cd, "carbon_price")
            }
        Else
            r.error = ParseError(json)
        End If

        Return r
    End Function

    Private Shared Function ParseError(j As JsonObject) As cCBAM_Error
        Dim e = j.ObjectOf("error")
        Return New cCBAM_Error With {
            .code = e.StringOf("code"),
            .message = e.StringOf("message"),
            .statusCode = e.IntOf("statusCode")
        }
    End Function

    Private Shared Function GetDec(o As JsonObject, n As String) As Decimal
        Dim s = o.StringOf(n).Replace(",", ".")
        Dim v As Decimal
        Decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, v)
        Return v
    End Function

    Private Shared Function GetNullableDec(o As JsonObject, n As String) As Decimal?
        Dim s = o.StringOf(n)
        If String.IsNullOrWhiteSpace(s) Then Return Nothing
        Return GetDec(o, n)
    End Function

End Class

' ============================================================================
'  DTOs
' ============================================================================
Public Class cCBAM_CostCalculation_Request
    Public Property cn_code As String
    Public Property country_code As String
    Public Property weight As Decimal
    Public Property see_total As Decimal?
    Public Property year As Integer?
    Public Property benchmark_value As Decimal?

    Public Function ToJson() As String
        Dim j As New JsonObject()
        j.UpdateString("cn_code", cn_code)
        j.UpdateString("country_code", country_code)
        j.UpdateNumber("weight", weight.ToString(CultureInfo.InvariantCulture))
        If see_total.HasValue Then j.UpdateNumber("see_total", see_total.Value.ToString(CultureInfo.InvariantCulture))
        If year.HasValue Then j.UpdateInt("year", year.Value)
        If benchmark_value.HasValue Then j.UpdateNumber("benchmark_value", benchmark_value.Value.ToString(CultureInfo.InvariantCulture))
        Return j.Emit()
    End Function
End Class

Public Class cCBAM_CostCalculation_Response
    Public Property success As Boolean
    Public Property data As cCBAM_CostCalculation_Data
    Public Property [error] As cCBAM_Error
End Class

Public Class cCBAM_CostCalculation_Data
    Public Property cost As Decimal
    Public Property cbam_emission As Decimal
    Public Property benchmark As Decimal
    Public Property phase_factor As Decimal
    Public Property carbon_price As Decimal
    Public Property currency As String
    Public Property info_message As String
    Public Property calculation_details As cCBAM_CostCalculation_Details
End Class

Public Class cCBAM_CostCalculation_Details
    Public Property cbam_emission As Decimal
    Public Property benchmark As Decimal
    Public Property adjusted_benchmark As Decimal
    Public Property emission_difference As Decimal
    Public Property weight As Decimal
    Public Property carbon_price As Decimal
End Class

Public Class cCBAM_CnCode_Response
    Public Property success As Boolean
    Public Property data As List(Of cCBAM_CnCode)
    Public Property [error] As cCBAM_Error
End Class

Public Class cCBAM_CnCode
    Public Property cn_code As String
    Public Property cn_description As String
End Class

Public Class cCBAM_CnCodeDefaults_Response
    Public Property success As Boolean
    Public Property data As cCBAM_CnCodeDefaults
    Public Property [error] As cCBAM_Error
End Class

Public Class cCBAM_CnCodeDefaults
    Public Property default_emission As Decimal
    Public Property benchmark As Decimal?
End Class

Public Class cCBAM_Error
    Public Property code As String
    Public Property message As String
    Public Property statusCode As Integer
End Class
