// Copyright 2020-2026 Buf Technologies, Inc.
//
// 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.

package buflsp

import (
	"go.lsp.dev/protocol"
	"gopkg.in/yaml.v3"
)

const bufLockDocsURL = "https://buf.build/docs/configuration/v2/buf-lock/"

// bufLockTopLevelDocs maps top-level buf.lock keys to their documentation.
var bufLockTopLevelDocs = map[string]bufYAMLDoc{
	"version": {summary: "Defines the configuration format version. Must be `v2`, `v1`, or `v1beta1`.", valueType: "string", url: bufLockDocsURL + "#version"},
	"deps":    {summary: "Pinned module dependencies for the workspace. Each entry uniquely identifies a specific snapshot of a dependency. This file is generated by `buf dep update` and should not be edited by hand.", valueType: "[]object", url: bufLockDocsURL + "#deps"},
}

// bufLockDepDocs maps dep entry sub-keys to their documentation.
var bufLockDepDocs = map[string]bufYAMLDoc{
	"name":   {summary: "The full module identifier (e.g. `buf.build/googleapis/googleapis`).", valueType: "string", url: bufLockDocsURL + "#deps"},
	"commit": {summary: "The commit hash identifying the exact pinned version of this dependency.", valueType: "string", url: bufLockDocsURL + "#deps"},
	"digest": {summary: "A cryptographic digest of the dependency contents that protects against tampering.", valueType: "string", url: bufLockDocsURL + "#deps"},
}

// bufLockHover searches the parsed buf.lock document for hover information at
// the given position and returns a Hover response, or nil.
func bufLockHover(docNode *yaml.Node, pos protocol.Position) *protocol.Hover {
	if docNode.Kind != yaml.DocumentNode || len(docNode.Content) == 0 {
		return nil
	}
	mapping := docNode.Content[0]
	if mapping.Kind != yaml.MappingNode {
		return nil
	}
	for i := 0; i+1 < len(mapping.Content); i += 2 {
		keyNode := mapping.Content[i]
		valNode := mapping.Content[i+1]

		if yamlNodeContainsPosition(keyNode, pos) {
			if doc, ok := bufLockTopLevelDocs[keyNode.Value]; ok {
				return makeBufYAMLHover(keyNode.Value, doc, yamlNodeRange(keyNode))
			}
			return nil
		}

		// Only recurse into the deps sequence to handle dep entry sub-keys.
		if keyNode.Value == "deps" && valNode.Kind == yaml.SequenceNode {
			for _, item := range valNode.Content {
				if item.Kind != yaml.MappingNode {
					continue
				}
				for j := 0; j+1 < len(item.Content); j += 2 {
					depKey := item.Content[j]
					if yamlNodeContainsPosition(depKey, pos) {
						if doc, ok := bufLockDepDocs[depKey.Value]; ok {
							return makeBufYAMLHover(depKey.Value, doc, yamlNodeRange(depKey))
						}
						return nil
					}
				}
			}
		}
	}
	return nil
}
