/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { DiagnosticResults, MessageSeverityList, DiagnosticMessage, Range, MessageSeverity } from "./diagnostictypes.js"

type Logger = (severity:MessageSeverity, ...msg:string[]) => void
let logger: Logger = (_, ...msg) => console.log(...msg)

export function setFormatterLogger(log:Logger) {
    logger = log
}

export function outputDiagnosticResultsFormatted(result: DiagnosticResults): void {
    for (let message of result.entries) {
        outputDiagnosticMessageFormatted(message)
    }
    outputReadableTotals(result)
}

function outputReadableTotals(result: DiagnosticResults): void {
    let totals: string[] = []
    for (let k of MessageSeverityList) {
        totals.push(`${k}: ${result.totals[k]}`)
    }
    logger('information', totals.join(", "))
}

function lineDigitCount(message: DiagnosticMessage): number {
    let count = 0
    for (let part of message.parts) {
        let range = part.location.range
        if (range == null) {
            continue
        }
        count = Math.max(count, range.start.line.toString().length, range.end.line.toString().length)
    }
    return count
}

function paddedLineNo(digits: number, line: number): string {
    let s = line.toString()
    if (s.length < digits) {
        return " ".repeat(digits - s.length) + s
    }
    return s
}

function formatLine(digits: number, lines: string[], lineNo: number): string {
    return `${paddedLineNo(digits, lineNo)} | ${lines[lineNo-1]}`
}

function formatUnderline(indent: string, lines: string[], lineNo: number, range: Range, edgeChar: string, midChar: string, message: string): string {
    if (lineNo == range.start.line && lineNo == range.end.line) {
        let len = range.end.character - range.start.character + 1
        return `${indent} | ${" ".repeat(range.start.character - 1)}${edgeChar}${len > 2 ? midChar.repeat(len - 2) : ""}${len > 1 ? edgeChar : ""} ${message}`
    }
    if (lineNo == range.start.line) {
        let len = lines[lineNo-1].length - range.start.character
        return `${indent} | ${" ".repeat(range.start.character - 1)}${edgeChar}${len > 1 ? midChar.repeat(len - 1) : ""}`
    }
    if (lineNo == range.end.line) {
        let len = range.end.character
        return `${indent} | ${len > 1 ? midChar.repeat(len - 1) : ""}${edgeChar} ${message}`
    }
    return `${indent} | ${midChar.repeat(lines[lineNo-1].length)}`
}

export function outputDiagnosticMessageFormatted(message: DiagnosticMessage): void {
    if (message.parts.length == 0) {
        return
    }
    logger(message.severity, `${message.severity}[${message.code}]: ${message.codeDescription}`)
    let digits = lineDigitCount(message)
    let indent = " ".repeat(digits)
    let first: boolean = true
    let lastPath: string = ""
    for (let part of message.parts) {
        const location = part.location
        if (location.range != null && location.lines != null) {
            let range = location.range
            let lines = location.lines
            logger(message.severity, `${indent}${lastPath != part.location.documentPath ? "-->" : ":::"} ${part.location.documentPath}:${range.start.line}:${range.start.character}`)
            logger(message.severity, `${indent} |`)
            const last = Math.min(range.end.line + 1, lines.length - 1)
            for (let i = Math.max(range.start.line - 1, 1); i <= last; ++i) {
                logger(message.severity, formatLine(digits, lines, i))
                if (i >= range.start.line && i <= range.end.line) {
                    logger(message.severity, formatUnderline(indent, lines, i, range, "^", first ? "-" : "~", part.message))
                }
            }
        } else {
            logger(message.severity, `${indent}--> ${part.location.documentPath}`)
            if (message.parts.length > 1) {
                logger(message.severity, `${indent} # ${part.message}`)
            }
        }
        first = false
        lastPath = part.location.documentPath
    }
    logger(message.severity, `${indent} = ${message.parts[0].message}`)
    logger(message.severity)
}