package utils

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"github.com/chromedp/cdproto/page"
	"github.com/chromedp/chromedp"
)

func resolveBrowserExecPath() (string, error) {
	var candidates []string
	for _, envName := range []string{"CHROME_PATH", "EDGE_PATH", "CHROMIUM_PATH", "BROWSER_PATH"} {
		if value := strings.TrimSpace(os.Getenv(envName)); value != "" {
			candidates = append(candidates, value)
		}
	}

	for _, binaryName := range []string{"chrome", "chromium", "chromium-browser", "google-chrome", "google-chrome-stable", "msedge"} {
		if path, err := exec.LookPath(binaryName); err == nil {
			return path, nil
		}
	}

	switch runtime.GOOS {
	case "windows":
		candidates = append(candidates,
			`C:\Program Files\Google\Chrome\Application\chrome.exe`,
			`C:\Program Files (x86)\Google\Chrome\Application\chrome.exe`,
			`C:\Program Files\Microsoft\Edge\Application\msedge.exe`,
			`C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe`,
		)
	case "darwin":
		candidates = append(candidates,
			"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
			"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
			"/Applications/Chromium.app/Contents/MacOS/Chromium",
		)
	default:
		candidates = append(candidates,
			"/usr/bin/google-chrome",
			"/usr/bin/google-chrome-stable",
			"/usr/bin/chromium",
			"/usr/bin/chromium-browser",
			"/usr/bin/microsoft-edge",
			"/snap/bin/chromium",
		)
	}

	seen := map[string]struct{}{}
	for _, candidate := range candidates {
		candidate = strings.TrimSpace(candidate)
		if candidate == "" {
			continue
		}
		if _, ok := seen[candidate]; ok {
			continue
		}
		seen[candidate] = struct{}{}
		if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
			return candidate, nil
		}
	}

	return "", fmt.Errorf("browser executable not found; install Chrome/Edge or set CHROME_PATH/EDGE_PATH")
}

func (s *PdfGenerator) renderWithChromedp(opts PdfOptions, actions ...chromedp.Action) ([]byte, error) {
	s.pool <- struct{}{}
	defer func() { <-s.pool }()

	if err := s.validateOptions(&opts); err != nil {
		return nil, fmt.Errorf("invalid PDF options: %w", err)
	}

	browserExecPath, resolveErr := resolveBrowserExecPath()
	if resolveErr != nil {
		return nil, resolveErr
	}

	allocatorOptions := append(chromedp.DefaultExecAllocatorOptions[:],
		chromedp.ExecPath(browserExecPath),
		chromedp.Flag("disable-local-file-access", true),
	)
	allocatorContext, allocatorCancel := chromedp.NewExecAllocator(s.ctx, allocatorOptions...)
	ctx, cancel := chromedp.NewContext(allocatorContext)
	ctx, timeoutCancel := context.WithTimeout(ctx, time.Duration(opts.Timeout)*time.Second)
	cleanup := func() {
		timeoutCancel()
		cancel()
		allocatorCancel()
	}
	defer cleanup()

	var buf []byte
	actions = append(actions, chromedp.ActionFunc(func(ctx context.Context) error {
		var err error
		printParams := page.PrintToPDF().
			WithPrintBackground(opts.PrintBackground).
			WithPaperWidth(opts.PaperWidth).
			WithPaperHeight(opts.PaperHeight).
			WithMarginTop(opts.MarginTop).
			WithMarginBottom(opts.MarginBottom).
			WithMarginLeft(opts.MarginLeft).
			WithMarginRight(opts.MarginRight).
			WithLandscape(opts.Landscape).
			WithScale(opts.Scale)

		buf, _, err = printParams.Do(ctx)
		return err
	}))

	if err := chromedp.Run(ctx, actions...); err != nil {
		return nil, fmt.Errorf("chromedp error: %w", err)
	}

	return buf, nil
}
