package webui

import (
	"strings"

	"golang.org/x/net/html"
)

// stripRemoteImages walks already-sanitized HTML (htmlBodyPolicy has already removed
// scripts/event handlers/etc. — this never runs on untrusted-for-XSS-purposes input)
// and neutralizes any <img src="..."> that isn't a data: URI, renaming it to
// data-blocked-src so the browser never fetches it — a classic tracking-pixel/
// read-receipt vector otherwise. Reports whether anything was actually blocked, so
// the caller only shows a "Show images" banner when there's something to reveal.
//
// This runs as a second pass over the DOM rather than trying to make bluemonday's own
// policy conditionally reject remote img src — bluemonday composes URL-scheme rules
// globally per policy (AllowStandardURLs), and UGCPolicy already bakes in "img src
// follows the global scheme allowlist" internally, so cleanly restricting only img
// src to data: URIs while leaving other elements' href/src alone isn't something the
// policy API exposes directly. A dedicated pass keeps the two concerns (XSS
// sanitization vs. privacy-motivated image blocking) independent and easy to reason
// about separately.
func stripRemoteImages(sanitizedHTML string) (cleaned string, blocked bool) {
	doc, err := html.Parse(strings.NewReader(sanitizedHTML))
	if err != nil {
		return sanitizedHTML, false
	}
	var walk func(*html.Node)
	walk = func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "img" {
			for i, attr := range n.Attr {
				if attr.Key != "src" {
					continue
				}
				if strings.HasPrefix(attr.Val, "data:") {
					break
				}
				n.Attr[i].Key = "data-blocked-src"
				blocked = true
				break
			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			walk(c)
		}
	}
	walk(doc)
	if !blocked {
		return sanitizedHTML, false
	}

	var buf strings.Builder
	// html.Parse wraps a fragment in a full document (html>head,body) — render just
	// the body's children back out, matching what was originally passed in (a
	// fragment, not a full document).
	body := findBody(doc)
	if body == nil {
		return sanitizedHTML, false
	}
	for c := body.FirstChild; c != nil; c = c.NextSibling {
		if err := html.Render(&buf, c); err != nil {
			return sanitizedHTML, false
		}
	}
	return buf.String(), true
}

func findBody(n *html.Node) *html.Node {
	if n.Type == html.ElementNode && n.Data == "body" {
		return n
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		if b := findBody(c); b != nil {
			return b
		}
	}
	return nil
}
