// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package jobparser

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestParseUses(t *testing.T) {
	t.Run("LocalSameRepo", func(t *testing.T) {
		cases := []struct {
			name string
			in   string
			want UsesRef
		}{
			{
				name: "gitea dir, .yml",
				in:   "./.gitea/workflows/build.yml",
				want: UsesRef{Kind: UsesKindLocalSameRepo, Path: ".gitea/workflows/build.yml"},
			},
			{
				name: "github dir, .yml",
				in:   "./.github/workflows/build.yml",
				want: UsesRef{Kind: UsesKindLocalSameRepo, Path: ".github/workflows/build.yml"},
			},
			{
				name: "gitea dir, .yaml",
				in:   "./.gitea/workflows/build.yaml",
				want: UsesRef{Kind: UsesKindLocalSameRepo, Path: ".gitea/workflows/build.yaml"},
			},
			{
				name: "filename containing dots is allowed",
				in:   "./.gitea/workflows/foo..bar.yml",
				want: UsesRef{Kind: UsesKindLocalSameRepo, Path: ".gitea/workflows/foo..bar.yml"},
			},
			{
				name: "nested subdirectory",
				in:   "./.gitea/workflows/sub/build.yml",
				want: UsesRef{Kind: UsesKindLocalSameRepo, Path: ".gitea/workflows/sub/build.yml"},
			},
			{
				name: "leading/trailing whitespace is trimmed",
				in:   "  ./.gitea/workflows/build.yml  ",
				want: UsesRef{Kind: UsesKindLocalSameRepo, Path: ".gitea/workflows/build.yml"},
			},
		}
		for _, c := range cases {
			t.Run(c.name, func(t *testing.T) {
				got, err := ParseUses(c.in)
				require.NoError(t, err)
				assert.Equal(t, c.want, *got)
			})
		}
	})

	t.Run("LocalCrossRepo", func(t *testing.T) {
		cases := []struct {
			name string
			in   string
			want UsesRef
		}{
			{
				name: "gitea dir, simple ref",
				in:   "owner/repo/.gitea/workflows/build.yml@v1",
				want: UsesRef{
					Kind:  UsesKindLocalCrossRepo,
					Owner: "owner",
					Repo:  "repo",
					Path:  ".gitea/workflows/build.yml",
					Ref:   "v1",
				},
			},
			{
				name: "github dir, branch ref",
				in:   "owner/repo/.github/workflows/build.yml@main",
				want: UsesRef{
					Kind:  UsesKindLocalCrossRepo,
					Owner: "owner",
					Repo:  "repo",
					Path:  ".github/workflows/build.yml",
					Ref:   "main",
				},
			},
			{
				name: ".yaml extension",
				in:   "owner/repo/.gitea/workflows/build.yaml@abc123",
				want: UsesRef{
					Kind:  UsesKindLocalCrossRepo,
					Owner: "owner",
					Repo:  "repo",
					Path:  ".gitea/workflows/build.yaml",
					Ref:   "abc123",
				},
			},
			{
				name: "ref with slashes (refs/heads/feature)",
				in:   "owner/repo/.gitea/workflows/build.yml@refs/heads/feature",
				want: UsesRef{
					Kind:  UsesKindLocalCrossRepo,
					Owner: "owner",
					Repo:  "repo",
					Path:  ".gitea/workflows/build.yml",
					Ref:   "refs/heads/feature",
				},
			},
			{
				name: "nested subdirectory under workflows",
				in:   "owner/repo/.gitea/workflows/sub/build.yml@v1",
				want: UsesRef{
					Kind:  UsesKindLocalCrossRepo,
					Owner: "owner",
					Repo:  "repo",
					Path:  ".gitea/workflows/sub/build.yml",
					Ref:   "v1",
				},
			},
		}
		for _, c := range cases {
			t.Run(c.name, func(t *testing.T) {
				got, err := ParseUses(c.in)
				require.NoError(t, err)
				assert.Equal(t, c.want, *got)
			})
		}
	})

	t.Run("Errors", func(t *testing.T) {
		cases := []struct {
			name string
			in   string
		}{
			{name: "empty string", in: ""},
			{name: "whitespace only", in: "   "},

			// Same-repo malformed
			{name: "same-repo with @ref", in: "./.gitea/workflows/build.yml@v1"},
			{name: "same-repo wrong directory", in: "./not-workflows/build.yml"},
			{name: "same-repo wrong extension", in: "./.gitea/workflows/build.txt"},
			{name: "same-repo missing extension", in: "./.gitea/workflows/build"},
			{name: "same-repo absolute path", in: "/.gitea/workflows/build.yml"},
			{name: "same-repo path traversal", in: "./.gitea/workflows/../escape.yml"},
			{name: "same-repo double slash", in: "./.gitea/workflows//build.yml"},
			{name: "same-repo redundant ./", in: "./.gitea/workflows/./build.yml"},
			{name: "same-repo no filename", in: "./.gitea/workflows/.yml"},

			// Cross-repo malformed
			{name: "cross-repo missing @ref", in: "owner/repo/.gitea/workflows/build.yml"},
			{name: "cross-repo empty ref", in: "owner/repo/.gitea/workflows/build.yml@"},
			{name: "cross-repo missing owner", in: "/repo/.gitea/workflows/build.yml@v1"},
			{name: "cross-repo missing repo", in: "owner//.gitea/workflows/build.yml@v1"},
			{name: "cross-repo wrong workflows dir", in: "owner/repo/workflows/build.yml@v1"},
			{name: "cross-repo wrong extension", in: "owner/repo/.gitea/workflows/build.txt@v1"},
			{name: "cross-repo path traversal", in: "owner/repo/.gitea/workflows/../escape.yml@v1"},
			{name: "cross-repo double slash in path", in: "owner/repo/.gitea/workflows//build.yml@v1"},
			// owner/repo with chars Gitea's name validators reject
			{name: "cross-repo owner with space", in: "bad owner/repo/.gitea/workflows/build.yml@v1"},
			{name: "cross-repo repo with @", in: "owner/re@po/.gitea/workflows/build.yml@v1"},
		}
		for _, c := range cases {
			t.Run(c.name, func(t *testing.T) {
				_, err := ParseUses(c.in)
				assert.Error(t, err)
			})
		}
	})
}
