// gen-manpage generates a markdown-formatted manpage, and validates the set of
// tags between the adequate binary and doc/tags.desc.
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math"
	"os"
	"os/exec"
	"strings"
)

type tagEntry struct {
	Name string `json:"Tag"`
	Desc string `json:"Description"`
	Ref  string `json:"References"`
}

func main() {
	out, err := exec.Command("../adequate", "--list-tags").Output()
	if err != nil {
		log.Fatal("../adequate: ", err)
	}

	buf, err1 := os.ReadFile("../doc/tags.desc")
	header, err2 := os.ReadFile("../doc/manpage.header")
	footer, err3 := os.ReadFile("../doc/manpage.footer")
	for _, err := range []error{err1, err2, err3} {
		if err != nil {
			log.Fatal("read: ", err)
		}
	}

	entries := []tagEntry{}
	err = json.Unmarshal(buf, &entries)
	if err != nil {
		if e, ok := err.(*json.SyntaxError); ok {
			// Print some context to help pinpoint where the syntax
			// error is.
			a := math.Float64bits(math.Max(0, float64(e.Offset-20)))
			b := math.Float64bits(math.Min(float64(len(buf)), float64(e.Offset+20)))
			log.Printf("Syntax error at byte offset %d: %q", e.Offset, buf[a:b])
		}
		log.Fatal("unmarshal: ", err)
	}

	binaryTags := make(map[string]bool)
	for _, t := range strings.Split(strings.Trim(string(out), "\n"), "\n") {
		binaryTags[t] = true
	}
	jsonTags := make(map[string]bool)
	for _, entry := range entries {
		jsonTags[entry.Name] = true
	}

	var missingTagDesc []string
	for t := range binaryTags {
		if !jsonTags[t] {
			missingTagDesc = append(missingTagDesc, t)
		}
	}
	if len(missingTagDesc) > 0 {
		log.Fatalf("The following tags are in the adequate binary but not in tags.desc: %v",
			strings.Join(missingTagDesc, ", "))
	}

	var staleTagDesc []string
	for t := range jsonTags {
		if !binaryTags[t] {
			staleTagDesc = append(staleTagDesc, t)
		}
	}
	if len(staleTagDesc) > 0 {
		log.Fatalf("The following tags are in tags.desc but not in the adequate binary: %s",
			strings.Join(staleTagDesc, ", "))
	}

	// all good, let's update the manpage markdown file
	md, err := os.OpenFile("../doc/adequate.md", os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		log.Fatal("open: ", err)
	}

	md.Write(header)
	for _, entry := range entries {
		md.WriteString(fmt.Sprintf("**%s**\n\n> %s\n\n", entry.Name, entry.Desc))
		if entry.Ref != "" {
			md.WriteString(fmt.Sprintf("> %s\n\n", entry.Ref))
		}
	}
	md.Write(footer)

	if err := md.Close(); err != nil {
		log.Fatal("close: ", err)
	}
}
