package rulelist_test

import (
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path/filepath"
	"sync/atomic"
	"testing"
	"time"

	"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
	"github.com/AdguardTeam/golibs/testutil"
	"github.com/stretchr/testify/require"
)

func TestMain(m *testing.M) {
	testutil.DiscardLogOutput(m)
}

// testTimeout is the common timeout for tests.
const testTimeout = 1 * time.Second

// testURLFilterID is the common [rulelist.URLFilterID] for tests.
const testURLFilterID rulelist.URLFilterID = 1

// testTitle is the common title for tests.
const testTitle = "Test Title"

// Common rule texts for tests.
const (
	testRuleTextBadTab      = "||bad-tab-and-comment.example^\t# A comment.\n"
	testRuleTextBlocked     = "||blocked.example^\n"
	testRuleTextBlocked2    = "||blocked-2.example^\n"
	testRuleTextEtcHostsTab = "0.0.0.0 tab..example^\t# A comment.\n"
	testRuleTextHTML        = "<!DOCTYPE html>\n"
	testRuleTextTitle       = "! Title:  " + testTitle + " \n"

	// testRuleTextCosmetic is a cosmetic rule with a zero-width non-joiner.
	//
	// See https://github.com/AdguardTeam/AdGuardHome/issues/6003.
	testRuleTextCosmetic = "||cosmetic.example## :has-text(/\u200c/i)\n"
)

// urlFilterIDCounter is the atomic integer used to create unique filter IDs.
var urlFilterIDCounter = &atomic.Int32{}

// newURLFilterID returns a new unique URLFilterID.
func newURLFilterID() (id rulelist.URLFilterID) {
	return rulelist.URLFilterID(urlFilterIDCounter.Add(1))
}

// newFilter is a helper for creating new filters in tests.  It does not
// register the closing of the filter using t.Cleanup; callers must do that
// either directly or by using the filter in an engine.
func newFilter(t testing.TB, u *url.URL, name string) (f *rulelist.Filter) {
	t.Helper()

	f, err := rulelist.NewFilter(&rulelist.FilterConfig{
		URL:         u,
		Name:        name,
		UID:         rulelist.MustNewUID(),
		URLFilterID: newURLFilterID(),
		Enabled:     true,
	})
	require.NoError(t, err)

	return f
}

// newFilterLocations is a test helper that sets up both the filtering-rule list
// file and the HTTP-server.  It also registers file removal and server stopping
// using t.Cleanup.
func newFilterLocations(
	t testing.TB,
	cacheDir string,
	fileData string,
	httpData string,
) (fileURL, srvURL *url.URL) {
	filePath := filepath.Join(cacheDir, "initial.txt")
	err := os.WriteFile(filePath, []byte(fileData), 0o644)
	require.NoError(t, err)

	testutil.CleanupAndRequireSuccess(t, func() (err error) {
		return os.Remove(filePath)
	})

	fileURL = &url.URL{
		Scheme: "file",
		Path:   filePath,
	}

	srv := newStringHTTPServer(httpData)
	t.Cleanup(srv.Close)

	srvURL, err = url.Parse(srv.URL)
	require.NoError(t, err)

	return fileURL, srvURL
}

// newStringHTTPServer returns a new HTTP server that serves s.
func newStringHTTPServer(s string) (srv *httptest.Server) {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		pt := testutil.PanicT{}

		_, err := io.WriteString(w, s)
		require.NoError(pt, err)
	}))
}
