//go:build ignore

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"go/format"
	"os"
	"regexp"
	"strings"
)

func version(src string) string {
	fp, err := os.Open(src + "/meson.build")
	if err != nil {
		panic(err)
	}
	defer fp.Close()

	var (
		// egrep "^\s+version: ['\"][0-9.]+['\"],?$" meson.build | egrep -o '[0-9.]+'
		reLine = regexp.MustCompile(`^\s+version: ['"][0-9.]+['"],?$`)
		reNum  = regexp.MustCompile(`[0-9.]+`)
		scan   = bufio.NewScanner(fp)
	)
	for scan.Scan() {
		line := scan.Text()
		if reLine.MatchString(line) {
			return reNum.FindString(line)
		}
	}
	if scan.Err() != nil {
		panic(scan.Err())
	}
	panic("version not found in meson.build")
}

func main() {
	// TODO: don't hard-code this. Or fetch from git or something.
	src := os.ExpandEnv("$HOME/src/postgresql")

	v := version(src)

	fp, err := os.Open(src + "/src/backend/utils/errcodes.txt")
	if err != nil {
		panic(err)
	}
	defer fp.Close()

	var (
		scan    = bufio.NewScanner(fp)
		re      = regexp.MustCompile(`_[^_]+`)
		out     = new(bytes.Buffer)
		codes   = new(bytes.Buffer)
		names   = new(bytes.Buffer)
		section string
	)
	fmt.Fprintf(out, "// Code generated by gen.go. DO NOT EDIT.\n\n// Last updated for PostgreSQL %s\n\n", v)
	out.WriteString("package pqerror\n\nvar (\n")
	for scan.Scan() {
		line := scan.Text()
		if line == "" || line[0] == '#' {
			continue
		}
		// Section: Class 01 - Warning
		// Section: Class 08 - Connection Exception
		if strings.HasPrefix(line, "Section: ") {
			section = line[9:]
			//fmt.Fprintf(names, "// %s\n", section)
			continue
		}

		// sqlstate E/W/S  errcode_macro_name              spec_name (optional)
		// 01000    W      ERRCODE_WARNING                 warning
		// 22022    E      ERRCODE_INDICATOR_OVERFLOW      indicator_overflow
		// 2202E    E      ERRCODE_ARRAY_ELEMENT_ERROR
		f := strings.Fields(line)
		if len(f) == 3 {
			continue
		}
		if len(f) != 4 {
			panic(fmt.Sprintf("invalid line: %q", line))
		}
		sqlstate, typ, name, spec := f[0], f[1], f[2], f[3]
		_ = typ // Not used atm

		name = strings.ToLower(name)
		name = strings.Replace(name, "errcode_", "_", 1)
		name = re.ReplaceAllStringFunc(name, func(m string) string {
			switch m {
			case "_xml", "_json", "_sql", "_fdw", "_io":
				return strings.ToUpper(m[1:])
			case "_sqlclient":
				return "SQLClient"
			case "_sqlserver":
				return "SQLServer"
			case "_sqlconnection":
				return "SQLConnection"
			case "_sqlstate":
				return "SQLSTATE"
			case "_plpgsql":
				return "PLpgSQL"
			}
			return string(m[1]^0x20) + m[2:]
		})

		class := strings.HasSuffix(sqlstate, "000")

		if class {
			fmt.Fprintf(out, "\tClass%s = Class(%q) // %s\n", name, sqlstate[:2], section[11:])
		}

		fmt.Fprintf(names, "\t%q: %q,\n", sqlstate, spec)

		fmt.Fprintf(codes, "\t%s = Code(%q)", name, sqlstate)
		if class {
			fmt.Fprintf(codes, " // %s", section)
		}
		fmt.Fprintln(codes)
	}
	if scan.Err() != nil {
		panic(scan.Err())
	}
	out.WriteString(")\n")

	out.WriteString("// A list of all error codes used in PostgreSQL.\n")
	out.WriteString("var (\n")
	out.Write(codes.Bytes())
	out.WriteString(")\n\n")

	out.WriteString("var errorCodeNames = map[Code]string{\n")
	out.Write(names.Bytes())
	out.WriteString("}\n")

	gosrc, err := format.Source(out.Bytes())
	if err != nil {
		panic(err)
	}

	err = os.WriteFile("codes.go", gosrc, 0o777)
	if err != nil {
		panic(err)
	}
}
