package tos_scraper

import (
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/http/cookiejar"
	"regexp"
	"strings"
	"time"

	"golang.org/x/net/html"

	"pluja.dev/kycnot.me/utils"
)

type ScraperResponse struct {
	Content string `json:"content"`
	Length  int    `json:"length"`
}

// Using the crawler API, get the HTML content of a given URL.
func GetBody(url string) (string, error) {
	// Send a GET request to os.Getenv("SCRAPER_API_URL")/scrap?url=url
	// Unmarshal the response and return the HTML content
	scraper_api := utils.Getenv("CRAWLER_API_URL", "http://localhost:3011")

	// Request the HTML content
	resp, err := http.Get(fmt.Sprintf("%v/scrap?url=%v", scraper_api, url))
	if err != nil {
		return "", err
	}

	// Read the response body
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	// Unmarshal the response
	var scraperResponse ScraperResponse
	err = json.Unmarshal(body, &scraperResponse)
	if err != nil {
		return "", err
	}

	// Return the HTML content
	return scraperResponse.Content, nil
}

// GetBodyHtml fetches the body content from a given URL, removes unwanted elements, and returns plain text.
func GetBodyHtml(url string) (string, error) {
	// Set user agent to avoid being blocked by Cloudflare
	jar, _ := cookiejar.New(nil)
	client := &http.Client{
		Jar: jar,
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", err
	}

	userAgents := []string{
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0",
		"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
		"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
	}

	s := rand.NewSource(time.Now().UnixNano())
	r := rand.New(s)

	req.Header.Set(
		"User-Agent",
		userAgents[r.Intn(len(userAgents))],
	)
	req.Header.Set(
		"Accept",
		"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
	)
	req.Header.Set("Accept-Language", "en-US,en;q=0.9")
	req.Header.Set("Cache-Control", "no-cache")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("DNT", "1")
	req.Header.Set("Referrer", fmt.Sprintf("https://www.google.com/search?q=%v", url))
	req.Header.Set("Upgrade-Insecure-Requests", "1")
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "", fmt.Errorf("status code error: %d %s", resp.StatusCode, resp.Status)
	}

	doc, err := html.Parse(resp.Body)
	if err != nil {
		return "", err
	}

	// Extract and return the text content
	textContent := extractTextContent(doc)

	// Compress whitespace in the resulting text
	compressedText := compressWhitespace(textContent)

	return compressedText, nil
}

// extractTextContent traverses the HTML DOM and extracts concatenated text content.
func extractTextContent(n *html.Node) string {
	if n.Type == html.TextNode {
		return n.Data
	}
	if n.Type == html.ElementNode {
		// Skip script and style elements
		if n.Data == "script" || n.Data == "style" || n.Data == "nav" || n.Data == "footer" || n.Data == "header" || n.Data == "head" {
			return ""
		}
	}

	var textContent string
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		textContent += extractTextContent(c)
	}

	return textContent
}

// compressWhitespace replaces sequences of whitespace with a single space.
func compressWhitespace(input string) string {
	space := regexp.MustCompile(`\s+`)
	return space.ReplaceAllString(strings.TrimSpace(input), " ")
}
