package main

import (
	"bytes"
	"errors"
	"fmt"
	"io/fs"
	"log/slog"
	"os"
	"reflect"
	"strings"

	"github.com/steveiliop56/tinyauth/internal/config"
)

type MarkdownEntry struct {
	Env         string
	Flag        string
	Description string
	Default     any
}

func generateMarkdown() {
	cfg := config.NewDefaultConfiguration()
	entries := make([]MarkdownEntry, 0)

	root := reflect.TypeOf(cfg).Elem()
	rootValue := reflect.ValueOf(cfg).Elem()
	rootPath := "tinyauth."

	walkAndBuild(root, rootValue, rootPath, &entries, buildMdEntry, buildMdMapEntry, buildMdChildPath)
	compiled := compileMd(entries)

	err := os.Remove("config.gen.md")
	if err != nil && !errors.Is(err, fs.ErrNotExist) {
		slog.Error("failed to remove example env file", "error", err)
		os.Exit(1)
	}

	err = os.WriteFile("config.gen.md", compiled, 0644)
	if err != nil {
		slog.Error("failed to write example env file", "error", err)
		os.Exit(1)
	}
}

func buildMdEntry(child reflect.StructField, childValue reflect.Value, parentPath string, entries *[]MarkdownEntry) {
	desc := child.Tag.Get("description")
	tag := child.Tag.Get("yaml")

	if tag == "-" {
		return
	}

	value := childValue.Interface()

	entry := MarkdownEntry{
		Env:         strings.ToUpper(strings.ReplaceAll(parentPath, ".", "_")) + strings.ToUpper(child.Name),
		Flag:        fmt.Sprintf("--%s%s", strings.TrimPrefix(parentPath, "tinyauth."), strings.ToLower(child.Name)),
		Description: desc,
	}

	switch childValue.Kind() {
	case reflect.Slice:
		sl, ok := value.([]string)
		if !ok {
			slog.Error("invalid default value", "value", value)
			return
		}
		entry.Default = fmt.Sprintf("`%s`", strings.Join(sl, ","))
	default:
		entry.Default = fmt.Sprintf("`%v`", value)
	}
	*entries = append(*entries, entry)
}

func buildMdMapEntry(child reflect.StructField, parentPath string, entries *[]MarkdownEntry) {
	fieldType := child.Type

	if fieldType.Key().Kind() != reflect.String {
		slog.Info("unsupported map key type", "type", fieldType.Key().Kind())
		return
	}

	tag := child.Tag.Get("yaml")

	if tag == "-" {
		return
	}

	mapPath := parentPath + tag + ".[name]."
	valueType := fieldType.Elem()

	if valueType.Kind() == reflect.Struct {
		zeroValue := reflect.New(valueType).Elem()
		walkAndBuild(valueType, zeroValue, mapPath, entries, buildMdEntry, buildMdMapEntry, buildMdChildPath)
	}
}

func buildMdChildPath(parent string, child string) string {
	return parent + strings.ToLower(child) + "."
}

func compileMd(entries []MarkdownEntry) []byte {
	buffer := bytes.Buffer{}

	buffer.WriteString("<!--- This file is automatically generated by gen/gen_md.go. Do not edit manually. --->\n\n")
	buffer.WriteString("# Tinyauth configuration reference\n\n")
	buffer.WriteString("| Environment | Flag | Description | Default |\n")
	buffer.WriteString("| - | - | - | - |\n")

	previousSection := ""

	for _, entry := range entries {
		if strings.Count(entry.Env, "_") > 1 {
			section := strings.Split(strings.TrimPrefix(entry.Env, "TINYAUTH_"), "_")[0]
			if section != previousSection {
				buffer.WriteString("\n## " + strings.ToLower(section) + "\n\n")
				buffer.WriteString("| Environment | Flag | Description | Default |\n")
				buffer.WriteString("| - | - | - | - |\n")
				previousSection = section
			}
		}
		fmt.Fprintf(&buffer, "| `%s` | `%s` | %s | %s |\n", entry.Env, entry.Flag, entry.Description, entry.Default)
	}

	return buffer.Bytes()
}
