package cmd

import (
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/salvacybersec/keyhunter/pkg/engine/sources"
)

func TestSelectSource_Directory(t *testing.T) {
	dir := t.TempDir()
	src, err := selectSource([]string{dir}, sourceFlags{})
	require.NoError(t, err)
	_, ok := src.(*sources.DirSource)
	require.True(t, ok, "expected *DirSource, got %T", src)
}

func TestSelectSource_File(t *testing.T) {
	dir := t.TempDir()
	f := filepath.Join(dir, "a.txt")
	require.NoError(t, os.WriteFile(f, []byte("x"), 0o644))
	src, err := selectSource([]string{f}, sourceFlags{})
	require.NoError(t, err)
	_, ok := src.(*sources.FileSource)
	require.True(t, ok, "expected *FileSource, got %T", src)
}

func TestSelectSource_Git(t *testing.T) {
	src, err := selectSource([]string{"./some-repo"}, sourceFlags{Git: true})
	require.NoError(t, err)
	gs, ok := src.(*sources.GitSource)
	require.True(t, ok, "expected *GitSource, got %T", src)
	require.Equal(t, "./some-repo", gs.RepoPath)
}

func TestSelectSource_GitSince(t *testing.T) {
	src, err := selectSource([]string{"./repo"}, sourceFlags{Git: true, Since: "2024-01-15"})
	require.NoError(t, err)
	gs := src.(*sources.GitSource)
	want, _ := time.Parse("2006-01-02", "2024-01-15")
	require.Equal(t, want, gs.Since)
}

func TestSelectSource_GitSinceBadFormat(t *testing.T) {
	_, err := selectSource([]string{"./repo"}, sourceFlags{Git: true, Since: "15/01/2024"})
	require.Error(t, err)
	require.Contains(t, err.Error(), "YYYY-MM-DD")
}

func TestSelectSource_URL(t *testing.T) {
	src, err := selectSource(nil, sourceFlags{URL: "https://example.com/a.js"})
	require.NoError(t, err)
	_, ok := src.(*sources.URLSource)
	require.True(t, ok)
}

func TestSelectSource_URLRejectsPositional(t *testing.T) {
	_, err := selectSource([]string{"./foo"}, sourceFlags{URL: "https://x"})
	require.Error(t, err)
}

func TestSelectSource_Clipboard(t *testing.T) {
	src, err := selectSource(nil, sourceFlags{Clipboard: true})
	require.NoError(t, err)
	_, ok := src.(*sources.ClipboardSource)
	require.True(t, ok)
}

func TestSelectSource_ClipboardRejectsPositional(t *testing.T) {
	_, err := selectSource([]string{"./foo"}, sourceFlags{Clipboard: true})
	require.Error(t, err)
}

func TestSelectSource_Stdin(t *testing.T) {
	for _, tok := range []string{"stdin", "-"} {
		src, err := selectSource([]string{tok}, sourceFlags{})
		require.NoError(t, err)
		_, ok := src.(*sources.StdinSource)
		require.True(t, ok, "token %q: expected *StdinSource, got %T", tok, src)
	}
}

func TestSelectSource_MutuallyExclusive(t *testing.T) {
	_, err := selectSource(nil, sourceFlags{Git: true, URL: "https://x"})
	require.Error(t, err)
	require.Contains(t, err.Error(), "mutually exclusive")
}

func TestSelectSource_MissingTarget(t *testing.T) {
	_, err := selectSource(nil, sourceFlags{})
	require.Error(t, err)
	require.Contains(t, err.Error(), "missing target")
}

func TestSelectSource_DirForwardsExcludes(t *testing.T) {
	dir := t.TempDir()
	src, err := selectSource([]string{dir}, sourceFlags{Excludes: []string{"*.log", "tmp/**"}})
	require.NoError(t, err)
	ds := src.(*sources.DirSource)
	// NewDirSource merges DefaultExcludes with extras, so user patterns must be present.
	found := 0
	for _, e := range ds.Excludes {
		if e == "*.log" || e == "tmp/**" {
			found++
		}
	}
	require.Equal(t, 2, found, "user excludes not forwarded, got %v", ds.Excludes)
}
