package services

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func TestCollyCrawlPreservesPoliciesAndRespectsRobots(t *testing.T) {
	hits := map[string]int{}
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		hits[r.URL.Path]++
		if r.URL.Path == "/robots.txt" {
			fmt.Fprint(w, "User-agent: *\nDisallow: /private\nAllow: /private/public\n")
			return
		}
		w.Header().Set("Content-Type", "text/html")
		switch r.URL.Path {
		case "/":
			fmt.Fprint(w, `<title>Bisnis uji</title><header>HEADER NOISE</header><nav><a href="/syarat-retur">Syarat</a><a href="/private">private</a><a href="/private/public">public</a><a href="https://elsewhere.invalid">external</a></nav><main><h1>Bisnis uji</h1><p>Konten utama untuk pelanggan.</p><a href="/syarat-retur#again">Duplikat</a></main><footer>FOOTER NOISE</footer>`)
		default:
			fmt.Fprint(w, `<main><h1>Syarat retur</h1><p>Retur tersedia untuk barang cacat.</p><p>Sertakan bukti pembelian dalam 7 hari.</p></main>`)
		}
	}))
	defer server.Close()
	pages := map[string]string{}
	failed := map[string]error{}
	err := crawlWebsite(server.URL, []string{server.URL + "/syarat-retur"}, 10, server.Client(), 0, func(u, title, content string, err error) {
		if err != nil {
			failed[u] = err
		} else {
			pages[u] = content
		}
	})
	if err != nil {
		t.Fatal(err)
	}
	if hits["/syarat-retur"] != 1 || hits["/private"] != 0 || hits["/private/public"] != 1 {
		t.Fatalf("visits: %v", hits)
	}
	if len(pages) != 3 || failed[server.URL+"/private"] == nil {
		t.Fatalf("pages=%v errors=%v", pages, failed)
	}
	root := pages[server.URL]
	if strings.Contains(root, "NOISE") || !strings.Contains(root, "Konten utama") {
		t.Fatalf("wrong extraction: %s", root)
	}
	if !strings.Contains(pages[server.URL+"/syarat-retur"], "\nSertakan") {
		t.Fatal("paragraph boundaries lost")
	}
}

func TestCollyCrawlLimitAndFailures(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/robots.txt" {
			w.WriteHeader(404)
			return
		}
		w.Header().Set("Content-Type", "text/html")
		switch r.URL.Path {
		case "/large":
			fmt.Fprint(w, strings.Repeat("a", maxPageBytes+100))
		case "/js":
			fmt.Fprint(w, `<div id="root"></div><script>renderApp()</script>`)
		case "/fail":
			w.WriteHeader(503)
		default:
			fmt.Fprint(w, `<main><p>Content</p></main>`)
		}
	}))
	defer server.Close()
	for _, path := range []string{"/large", "/js", "/fail"} {
		var got error
		count := 0
		err := crawlWebsiteWithRenderer(context.Background(), server.URL+path, nil, 1, server.Client(), 0, nil, func(_, _, _ string, e error) { got = e; count++ })
		if err != nil || got == nil || count != 1 {
			t.Fatalf("%s count=%d err=%v page=%v", path, count, err, got)
		}
	}
	count := 0
	_ = crawlWebsite(server.URL, []string{server.URL + "/a", server.URL + "/b"}, 2, server.Client(), 0, func(_, _, _ string, e error) { count++ })
	if count != 2 {
		t.Fatalf("limit ignored: %d", count)
	}
}

func TestCrawlerRejectsLocalNetworkAndExternalRedirects(t *testing.T) {
	c := newCrawlHTTPClient()
	if _, err := c.Get("http://127.0.0.1:3030"); err == nil {
		t.Fatal("local network allowed")
	}
	first, _ := http.NewRequest("GET", "https://example.com", nil)
	redirected, _ := http.NewRequest("GET", "https://other.example/path", nil)
	if crawlRedirect(redirected, []*http.Request{first}) == nil {
		t.Fatal("external redirect allowed")
	}
}

func TestScoreCustomerPoliciesRemainRecommended(t *testing.T) {
	content := "Kebijakan retur berlaku untuk produk cacat. Pelanggan wajib menyertakan bukti pembelian dan foto barang dalam 7 hari. Pengembalian diperiksa oleh tim layanan, biaya pengiriman mengikuti hasil pemeriksaan."
	for _, path := range []string{"/kebijakan-retur", "/syarat-dan-ketentuan", "/terms"} {
		sc := ScorePageForCSTraining("https://example.com"+path, "Syarat dan kebijakan retur", content)
		if !sc.Recommended {
			t.Fatalf("policy %s excluded: %+v", path, sc)
		}
	}
}

func TestNormalizeURLPreservesContentQuery(t *testing.T) {
	if got := normalizeURL("https://example.com/product?id=42&utm_source=chat#top"); got != "https://example.com/product?id=42" {
		t.Fatal(got)
	}
}
