package sources

import (
	"context"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/salvacybersec/keyhunter/pkg/types"
)

func drainURL(t *testing.T, src Source) ([]types.Chunk, error) {
	t.Helper()
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	out := make(chan types.Chunk, 65536)
	errCh := make(chan error, 1)
	go func() { errCh <- src.Chunks(ctx, out); close(out) }()
	var got []types.Chunk
	for c := range out {
		got = append(got, c)
	}
	return got, <-errCh
}

func TestURLSource_Fetches(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		_, _ = w.Write([]byte("API_KEY=sk-live-xyz"))
	}))
	defer srv.Close()

	chunks, err := drainURL(t, NewURLSource(srv.URL))
	require.NoError(t, err)
	require.Len(t, chunks, 1)
	require.Equal(t, "url:"+srv.URL, chunks[0].Source)
	require.Equal(t, "API_KEY=sk-live-xyz", string(chunks[0].Data))
}

func TestURLSource_RejectsBinaryContentType(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "image/png")
		_, _ = w.Write([]byte{0x89, 0x50, 0x4e, 0x47})
	}))
	defer srv.Close()

	_, err := drainURL(t, NewURLSource(srv.URL))
	require.Error(t, err)
	require.Contains(t, err.Error(), "Content-Type")
}

func TestURLSource_RejectsNonHTTPScheme(t *testing.T) {
	_, err := drainURL(t, NewURLSource("file:///etc/passwd"))
	require.Error(t, err)
	require.Contains(t, err.Error(), "unsupported scheme")
}

func TestURLSource_Rejects500(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "boom", http.StatusInternalServerError)
	}))
	defer srv.Close()

	_, err := drainURL(t, NewURLSource(srv.URL))
	require.Error(t, err)
	require.Contains(t, err.Error(), "500")
}

func TestURLSource_RejectsOversizeBody(t *testing.T) {
	// Serve body just over the cap.
	big := strings.Repeat("a", int(MaxURLContentLength)+10)
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		_, _ = w.Write([]byte(big))
	}))
	defer srv.Close()

	_, err := drainURL(t, NewURLSource(srv.URL))
	require.Error(t, err)
}

func TestURLSource_FollowsRedirect(t *testing.T) {
	target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		_, _ = w.Write([]byte("redirected body"))
	}))
	defer target.Close()

	redirector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, target.URL, http.StatusMovedPermanently)
	}))
	defer redirector.Close()

	chunks, err := drainURL(t, NewURLSource(redirector.URL))
	require.NoError(t, err)
	require.NotEmpty(t, chunks)
	require.Contains(t, string(chunks[0].Data), "redirected body")
}
