package web_test

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

	"github.com/stretchr/testify/require"

	"git.d-ma.be/mathias/tapir/internal/domain"
)

// fakeFetcher is a web.VideoFetcher returning a fixed video (or an error),
// scoped to whatever (userID, videoID) the handler asks for.
type fakeFetcher struct {
	title string
	err   error
	calls int
}

func (f *fakeFetcher) FetchVideo(_ context.Context, userID, videoID string) (domain.Video, error) {
	f.calls++
	if f.err != nil {
		return domain.Video{}, f.err
	}
	return domain.Video{
		UserID:          userID,
		Provider:        domain.ProviderYouTube,
		ProviderVideoID: videoID,
		Title:           f.title,
		URL:             "https://www.youtube.com/watch?v=" + videoID,
	}, nil
}

func pasteReq(rawURL string) *http.Request {
	req := httptest.NewRequest(http.MethodPost, "/paste",
		strings.NewReader("url="+url.QueryEscape(rawURL)))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	return req
}

func TestPasteValidURLAddsAndRequests(t *testing.T) {
	ctx := context.Background()
	app := newApp(t)
	resetDB(t, rawPool(t))
	app.Fetcher = &fakeFetcher{title: "Pasted Talk"}
	p := rawPool(t)

	rec := do(t, app, pasteReq("https://youtu.be/dQw4w9WgXcQ"))
	require.Equal(t, http.StatusOK, rec.Code)

	var (
		count, requested int
		title            string
	)
	require.NoError(t, p.QueryRow(ctx,
		`SELECT count(*), coalesce(max(title),'') FROM videos
		  WHERE user_id=$1 AND provider_video_id='dQw4w9WgXcQ'`, userID).Scan(&count, &title))
	require.Equal(t, 1, count, "pasted video added once, scoped to the user")
	require.Equal(t, "Pasted Talk", title)

	require.NoError(t, p.QueryRow(ctx,
		`SELECT count(*) FROM videos
		  WHERE user_id=$1 AND provider_video_id='dQw4w9WgXcQ' AND summarize_requested`,
		userID).Scan(&requested))
	require.Equal(t, 1, requested, "pasted video is queued for summarization (through the gate)")
}

func TestPasteInvalidURLRejected(t *testing.T) {
	app := newApp(t)
	resetDB(t, rawPool(t))
	app.Fetcher = &fakeFetcher{title: "x"}

	rec := do(t, app, pasteReq("definitely not a url"))
	require.Equal(t, http.StatusBadRequest, rec.Code)

	var count int
	require.NoError(t, rawPool(t).QueryRow(context.Background(),
		`SELECT count(*) FROM videos WHERE user_id=$1`, userID).Scan(&count))
	require.Equal(t, 0, count, "invalid input adds nothing")
}

func TestPasteVideoNotFound(t *testing.T) {
	app := newApp(t)
	resetDB(t, rawPool(t))
	app.Fetcher = &fakeFetcher{err: domain.ErrVideoNotFound}

	rec := do(t, app, pasteReq("https://youtu.be/dQw4w9WgXcQ"))
	require.Equal(t, http.StatusNotFound, rec.Code)

	var count int
	require.NoError(t, rawPool(t).QueryRow(context.Background(),
		`SELECT count(*) FROM videos WHERE user_id=$1`, userID).Scan(&count))
	require.Equal(t, 0, count, "a not-found video adds nothing")
}

func TestPasteDedupNoDuplicate(t *testing.T) {
	app := newApp(t)
	resetDB(t, rawPool(t))
	app.Fetcher = &fakeFetcher{title: "Pasted Talk"}

	require.Equal(t, http.StatusOK, do(t, app, pasteReq("https://youtu.be/dQw4w9WgXcQ")).Code)
	require.Equal(t, http.StatusOK, do(t, app, pasteReq("https://www.youtube.com/watch?v=dQw4w9WgXcQ")).Code)

	var count int
	require.NoError(t, rawPool(t).QueryRow(context.Background(),
		`SELECT count(*) FROM videos WHERE user_id=$1 AND provider_video_id='dQw4w9WgXcQ'`,
		userID).Scan(&count))
	require.Equal(t, 1, count, "pasting the same video twice must not duplicate the row")
}

func TestListShowsPasteFormForConnectedUserWithVideos(t *testing.T) {
	app := newApp(t)
	resetDB(t, rawPool(t))
	app.Fetcher = &fakeFetcher{title: "x"}
	p := rawPool(t)

	// Connected user with a non-empty feed (the case the bug missed: hasConnected
	// was only computed for an empty feed).
	_, err := p.Exec(context.Background(),
		`INSERT INTO video_connections (user_id, provider, token_ref, status)
		 VALUES ($1, 'youtube', 'youtube/x/refresh_token', 'active')`, userID)
	require.NoError(t, err)
	seedVideo(t, p, "11111111-1111-1111-1111-111111111111", "A talk", "https://youtu.be/aaaaaaaaaaa", time.Now())

	rec := do(t, app, httptest.NewRequest(http.MethodGet, "/", nil))
	require.Equal(t, http.StatusOK, rec.Code)
	require.Contains(t, body(t, rec), `action="/paste"`,
		"a connected user must see the paste box even when the feed has videos")
}
