// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package main

import (
	"encoding/json"
	"fmt"

	"github.com/hashicorp/hcl/v2"
)

func decodeJSONDiagnostics(src []byte) hcl.Diagnostics {
	type PosJSON struct {
		Line   int `json:"line"`
		Column int `json:"column"`
		Byte   int `json:"byte"`
	}
	type RangeJSON struct {
		Filename string  `json:"filename"`
		Start    PosJSON `json:"start"`
		End      PosJSON `json:"end"`
	}
	type DiagnosticJSON struct {
		Severity string     `json:"severity"`
		Summary  string     `json:"summary"`
		Detail   string     `json:"detail,omitempty"`
		Subject  *RangeJSON `json:"subject,omitempty"`
	}
	type DiagnosticsJSON struct {
		Diagnostics []DiagnosticJSON `json:"diagnostics"`
	}

	var raw DiagnosticsJSON
	var diags hcl.Diagnostics
	err := json.Unmarshal(src, &raw)
	if err != nil {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Failed to parse hcldec diagnostics result",
			Detail:   fmt.Sprintf("Sub-program hcldec produced invalid diagnostics: %s.", err),
		})
		return diags
	}

	if len(raw.Diagnostics) == 0 {
		return nil
	}

	diags = make(hcl.Diagnostics, 0, len(raw.Diagnostics))
	for _, rawDiag := range raw.Diagnostics {
		var severity hcl.DiagnosticSeverity
		switch rawDiag.Severity {
		case "error":
			severity = hcl.DiagError
		case "warning":
			severity = hcl.DiagWarning
		default:
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Failed to parse hcldec diagnostics result",
				Detail:   fmt.Sprintf("Diagnostic has unsupported severity %q.", rawDiag.Severity),
			})
			continue
		}

		diag := &hcl.Diagnostic{
			Severity: severity,
			Summary:  rawDiag.Summary,
			Detail:   rawDiag.Detail,
		}
		if rawDiag.Subject != nil {
			rawRange := rawDiag.Subject
			diag.Subject = &hcl.Range{
				Filename: rawRange.Filename,
				Start: hcl.Pos{
					Line:   rawRange.Start.Line,
					Column: rawRange.Start.Column,
					Byte:   rawRange.Start.Byte,
				},
				End: hcl.Pos{
					Line:   rawRange.End.Line,
					Column: rawRange.End.Column,
					Byte:   rawRange.End.Byte,
				},
			}
		}
		diags = append(diags, diag)
	}

	return diags
}

func severityString(severity hcl.DiagnosticSeverity) string {
	switch severity {
	case hcl.DiagError:
		return "error"
	case hcl.DiagWarning:
		return "warning"
	default:
		return "unsupported-severity"
	}
}

func rangeString(rng hcl.Range) string {
	return fmt.Sprintf(
		"from line %d column %d byte %d to line %d column %d byte %d",
		rng.Start.Line, rng.Start.Column, rng.Start.Byte,
		rng.End.Line, rng.End.Column, rng.End.Byte,
	)
}
