package templatehandler

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"testing"

	"github.com/mrjosh/helm-ls/internal/adapter/yamlls"
	"github.com/mrjosh/helm-ls/internal/charts"
	"github.com/mrjosh/helm-ls/internal/lsp/document"
	"github.com/mrjosh/helm-ls/internal/testutil"
	"github.com/mrjosh/helm-ls/internal/util"
	"github.com/stretchr/testify/assert"
	lsp "go.lsp.dev/protocol"
	"go.lsp.dev/uri"
)

var (
	rootUri           = uri.File("../../../testdata/dependenciesExample/")
	fileURI           = uri.File("../../../testdata/dependenciesExample/templates/deployment.yaml")
	fileURIInSubchart = uri.File("../../../testdata/dependenciesExample/charts/subchartexample/templates/subchart.yaml")
)

type testCase struct {
	// Must be content of a line in the file fileURI
	templateLineWithMarker string
	expectedFile           string
	expectedFileCount      int
	expectedStartPosition  lsp.Position
	expectedError          error
	inSubchart             bool
}

// Test definition on a real chart found in $rootUri
func TestDefinitionChart(t *testing.T) {
	testCases := []testCase{
		{
			`{{ include "common.na^mes.name" . }}`,
			"charts/.helm_ls_cache/common/templates/_names.tpl",
			1,
			lsp.Position{Line: 9, Character: 0},
			nil,
			false,
		},
		{
			`{{- include "dependenciesEx^ample.labels" . | nindent 4 }}`,
			"templates/_helpers.tpl",
			1,
			lsp.Position{Line: 35, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Values.gl^obal.subchart }}`,
			"values.yaml",
			3,
			lsp.Position{Line: 7, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Values.gl^obal.subchart }}`,
			"charts/subchartexample/values.yaml",
			3,
			lsp.Position{Line: 0, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Values.common.exa^mpleValue }}`,
			"charts/.helm_ls_cache/common/values.yaml",
			1,
			// this tests, that the file also contains comments
			lsp.Position{Line: 7, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Values.comm^on.exampleValue }}`,
			"charts/.helm_ls_cache/common/values.yaml",
			1,
			lsp.Position{Line: 7, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Values.subch^artexample.subchartWithoutGlobal }}`,
			"values.yaml",
			3,
			lsp.Position{Line: 54, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Values.subch^artexample.subchartWithoutGlobal }}`,
			"charts/subchartexample/values.yaml",
			3,
			lsp.Position{Line: 0, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Values.subchartexample.subchartWith^outGlobal }}`,
			"values.yaml",
			2,
			lsp.Position{Line: 55, Character: 2},
			nil,
			false,
		},
		{
			`{{ .Values.subchartexample.subchart^WithoutGlobal }}`,
			"charts/subchartexample/values.yaml",
			2,
			lsp.Position{Line: 3, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Cha^rt.Name }}`,
			"Chart.yaml",
			1,
			lsp.Position{Line: 0, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Chart.Na^me }}`,
			"Chart.yaml",
			1,
			lsp.Position{Line: 1, Character: 0},
			nil,
			false,
		},
		{
			`{{ .Values.subchart^WithoutGlobal }}`,
			"charts/subchartexample/values.yaml",
			2,
			lsp.Position{Line: 3, Character: 0},
			nil,
			true,
		},
	}

	for _, tc := range testCases {
		t.Run("Definition on "+tc.templateLineWithMarker, func(t *testing.T) {
			uri := fileURI
			if tc.inSubchart {
				uri = fileURIInSubchart
			}
			fileContent, err := os.ReadFile(uri.Filename())
			if err != nil {
				t.Fatal(err)
			}

			pos, found := testutil.GetPositionOfMarkedLineInFile(
				string(fileContent), tc.templateLineWithMarker, "^")
			if !found {
				t.Fatalf("%s is not in the file %s", tc.templateLineWithMarker, fileURI.Filename())
			}

			documents := document.NewDocumentStore()

			chart := charts.NewChart(rootUri, util.DefaultConfig.ValuesFilesConfig)

			addChartCallback := func(chart *charts.Chart) {}
			chartStore := charts.NewChartStore(rootUri, charts.NewChart, addChartCallback)
			_, err = chartStore.GetChartForURI(rootUri)
			h := &TemplateHandler{
				chartStore:      chartStore,
				documents:       documents,
				yamllsConnector: &yamlls.Connector{},
			}

			assert.NoError(t, err)

			documents.LoadDocsOnNewChart(chart, util.DefaultConfig)

			locations, err := h.Definition(context.TODO(), &lsp.DefinitionParams{
				TextDocumentPositionParams: lsp.TextDocumentPositionParams{
					TextDocument: lsp.TextDocumentIdentifier{URI: uri},
					Position:     pos,
				},
			})

			assert.Equal(t, tc.expectedError, err)
			assert.Len(t, locations, tc.expectedFileCount)

			// find the location with the correct file path
			foundLocation := false
			for _, location := range locations {
				if location.URI.Filename() == filepath.Join(rootUri.Filename(), tc.expectedFile) {
					locations = []lsp.Location{location}
					foundLocation = true
					break
				}
			}

			assert.True(t, foundLocation, fmt.Sprintf("Did not find a result with the expected file path %s ", filepath.Join(rootUri.Filename(), tc.expectedFile)))

			if len(locations) > 0 {
				assert.Equal(t, filepath.Join(rootUri.Filename(), tc.expectedFile), locations[0].URI.Filename())
				assert.Equal(t, tc.expectedStartPosition, locations[0].Range.Start)
			}

			for _, location := range locations {
				assert.FileExists(t, location.URI.Filename())
			}

			os.RemoveAll(filepath.Join(rootUri.Filename(), "charts", charts.DependencyCacheFolder))
		})
	}
}
