Diagnostics

use "immutable-json"
use "ast"

class ref Diagnostic
    """
    See: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnostic
    """
    let range: LspPositionRange
    let severity: I64
    // left out intentionally
    // let code: (I64 | String | None)
    // let code_description: (CodeDescription | None)
    let message: String
    let related_information: Array[DiagnosticRelatedInformation]

    new ref create(range': LspPositionRange, severity': I64, message': String) =>
        range = range'
        severity = severity'
        message = message'
        related_information = Array[DiagnosticRelatedInformation].create(0)

    new val from_error(err: Error) =>
        this.range = LspPositionRange.from_single_pos(LspPosition.from_ast_pos(err.position))
        this.severity = DiagnosticSeverities.err()
        this.message = err.msg
        this.related_information = Array[DiagnosticRelatedInformation].create(err.infos.size())
        for info in err.infos.values() do
            let file_path = 
                try 
                    info.file as String
                else
                    try
                        err.file as String
                    else
                        continue
                    end
                end
            let file = Uris.from_path(file_path)
            let location = LspLocation.create(
                file, 
                LspPositionRange.from_single_pos(
                    LspPosition.from_ast_pos(info.position)
                )
            )
            this.related_information.push(
                DiagnosticRelatedInformation.create(location, info.msg)
            )
        end

    fun to_json(): JsonType =>
        var builder = Obj("range", this.range.to_json())(
            "severity", this.severity)(
            "source", "pony-lsp")(
            "message", this.message)
        if this.related_information.size() > 0 then
            var arr = Arr
            for rel_info in this.related_information.values() do
                arr = arr(rel_info.to_json())
            end
            builder = builder("relatedInformation", arr)
        end
        builder.build()



primitive DiagnosticSeverities
    fun tag err(): I64 => 1
    fun tag warning(): I64 => 2
    fun tag information(): I64 => 2
    fun tag hint(): I64 => 2

class val DiagnosticRelatedInformation
    let location: LspLocation
    let message: String

    new create(location': LspLocation, message': String) =>
        location = location'
        message = message'

    fun to_json(): JsonType =>
        Obj("location", this.location.to_json())(
            "message", this.message).build()