package main

import (
    "context"
    "flag"
    "fmt"
    "log"
    "os"
    "os/exec"
    "path/filepath"
    "strings"

    "smartbar/internal/config"

    "github.com/chromedp/cdproto/emulation"
    "github.com/chromedp/cdproto/page"
    "github.com/chromedp/chromedp"
    pdfapi "github.com/pdfcpu/pdfcpu/pkg/api"
)

func must(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

func parseYAMLListOfStrings(src string) ([]string, error) {
    var out []string
    for _, line := range strings.Split(src, "\n") {
        line = strings.TrimSpace(line)
        if line == "" || strings.HasPrefix(line, "#") {
            continue
        }
        if strings.HasPrefix(line, "-") {
            item := strings.TrimSpace(strings.TrimPrefix(line, "-"))
            if item != "" {
                out = append(out, item)
            }
        } else {
            return nil, fmt.Errorf("invalid yaml line (expect '- item'): %s", line)
        }
    }
    return out, nil
}

func findChromeExec() (string, error) {
    if v := os.Getenv("CHROME_PATH"); v != "" {
        return v, nil
    }
    candidates := []string{"google-chrome-stable", "google-chrome", "chromium-browser", "chromium", "chrome"}
    for _, name := range candidates {
        if p, err := exec.LookPath(name); err == nil {
            return p, nil
        }
    }
    // macOS common app bundle paths
    macPaths := []string{
        "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
        "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
        "/Applications/Chromium.app/Contents/MacOS/Chromium",
    }
    for _, p := range macPaths {
        if _, err := os.Stat(p); err == nil {
            return p, nil
        }
    }
    return "", fmt.Errorf("no Chrome/Chromium executable found; set CHROME_PATH or install chromium/google-chrome")
}
 
func main() {
    var (
        pagesDir string
        orderPath string
        outPDF string
        width  float64
        height float64
    )
    flag.StringVar(&pagesDir, "pages", "_dist", "path to compiled pages directory (required)")
    flag.StringVar(&orderPath, "order", "pages.yaml", "path to YAML file listing page order (required)")
    flag.StringVar(&outPDF, "out", "_dist/output.pdf", "output PDF path (required)")
    flag.Float64Var(&width, "w", config.PageWidthIn, "page width in inches")
    flag.Float64Var(&height, "h", config.PageHeightIn, "page height in inches")
    flag.Parse()

    if pagesDir == "" || orderPath == "" || outPDF == "" {
        log.Fatal("--pages, --order and --out are required")
    }

    // Load order
    data, err := os.ReadFile(orderPath)
    must(err)
	
    ordered, err := parseYAMLListOfStrings(string(data))
    must(err)

    // Resolve Chrome
    chromeExec, err := findChromeExec()
    must(err)
	
    opts := append(chromedp.DefaultExecAllocatorOptions[:], chromedp.ExecPath(chromeExec), chromedp.Flag("headless", true), chromedp.Flag("disable-gpu", true), chromedp.Flag("hide-scrollbars", true))
    allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
    defer cancel()

    ctx, cancel := chromedp.NewContext(allocCtx)
    defer cancel()

    // Temp dir for individual PDFs
    tmpDir, err := os.MkdirTemp("", "smartbar_pdfs_*")
    must(err)
    defer os.RemoveAll(tmpDir)

    var partPDFs []string
    for _, rel := range ordered {
        abs := filepath.Join(pagesDir, rel)
        if _, err := os.Stat(abs); err != nil {
            log.Printf("skip missing page: %s", rel)
            continue
        }
        absHTML, _ := filepath.Abs(abs)
        url := "file://" + absHTML

        outPath := filepath.Join(tmpDir, strings.TrimSuffix(filepath.Base(rel), filepath.Ext(rel)) + ".pdf")
        // Render one page PDF via printToPDF
        err := chromedp.Run(ctx, chromedp.Tasks{
            chromedp.Navigate(url),
            chromedp.ActionFunc(func(ctx context.Context) error {
                if err := emulation.SetEmulatedMedia().WithMedia("print").Do(ctx); err != nil {
                    return err
                }
                params := page.PrintToPDF().
                    WithPaperWidth(width).
                    WithPaperHeight(height).
                    WithMarginTop(0).WithMarginBottom(0).WithMarginLeft(0).WithMarginRight(0).
                    WithPrintBackground(true)
                buf, _, err := params.Do(ctx)
                if err != nil {
                    return err
                }
                return os.WriteFile(outPath, buf, 0o644)
            }),
        })
        must(err)
        partPDFs = append(partPDFs, outPath)
    }

    if len(partPDFs) == 0 {
        log.Fatal("no pages produced; check order file")
    }

    // Merge PDFs into single output
    must(os.MkdirAll(filepath.Dir(outPDF), 0o755))

    // pdfcpu expects []string in order and writes outPDF
    must(pdfapi.MergeCreateFile(partPDFs, outPDF, false, nil))
    fmt.Printf("Wrote %s (%d pages)\n", outPDF, len(partPDFs))
}


