// ALR - Any Linux Repository
// Copyright (C) 2025 The ALR Authors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see .
package main
import (
	"bytes"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"log"
	"os"
	"reflect"
	"strings"
	"text/template"
)
func resolvedStructGenerator(buf *bytes.Buffer, fields []*ast.Field) {
	contentTemplate := template.Must(template.New("").Parse(`
type {{ .EntityNameLower }}Resolved struct {
{{ .StructFields }}
}
`))
	var structFieldsBuilder strings.Builder
	for _, field := range fields {
		for _, name := range field.Names {
			// Поле с типом
			fieldTypeStr := exprToString(field.Type)
			// Структура поля
			var buf bytes.Buffer
			buf.WriteString("\t")
			buf.WriteString(name.Name)
			buf.WriteString(" ")
			buf.WriteString(fieldTypeStr)
			// Обработка json-тега
			jsonTag := ""
			if field.Tag != nil {
				raw := strings.Trim(field.Tag.Value, "`")
				tag := reflect.StructTag(raw)
				if val := tag.Get("json"); val != "" {
					jsonTag = val
				}
			}
			if jsonTag == "" {
				jsonTag = strings.ToLower(name.Name)
			}
			buf.WriteString(fmt.Sprintf(" `json:\"%s\"`", jsonTag))
			buf.WriteString("\n")
			structFieldsBuilder.Write(buf.Bytes())
		}
	}
	params := struct {
		EntityNameLower string
		StructFields    string
	}{
		EntityNameLower: "package",
		StructFields:    structFieldsBuilder.String(),
	}
	err := contentTemplate.Execute(buf, params)
	if err != nil {
		log.Fatalf("execute template: %v", err)
	}
}
func toResolvedFuncGenerator(buf *bytes.Buffer, fields []*ast.Field) {
	contentTemplate := template.Must(template.New("").Parse(`
func {{ .EntityName }}ToResolved(src *{{ .EntityName }}) {{ .EntityNameLower }}Resolved {
	return {{ .EntityNameLower }}Resolved{
{{ .Assignments }}
	}
}
`))
	var assignmentsBuilder strings.Builder
	for _, field := range fields {
		for _, name := range field.Names {
			var assignBuf bytes.Buffer
			assignBuf.WriteString("\t\t")
			assignBuf.WriteString(name.Name)
			assignBuf.WriteString(": ")
			if isOverridableField(field.Type) {
				assignBuf.WriteString(fmt.Sprintf("src.%s.Resolved()", name.Name))
			} else {
				assignBuf.WriteString(fmt.Sprintf("src.%s", name.Name))
			}
			assignBuf.WriteString(",\n")
			assignmentsBuilder.Write(assignBuf.Bytes())
		}
	}
	params := struct {
		EntityName      string
		EntityNameLower string
		Assignments     string
	}{
		EntityName:      "Package",
		EntityNameLower: "package",
		Assignments:     assignmentsBuilder.String(),
	}
	err := contentTemplate.Execute(buf, params)
	if err != nil {
		log.Fatalf("execute template: %v", err)
	}
}
func resolveFuncGenerator(buf *bytes.Buffer, fields []*ast.Field) {
	contentTemplate := template.Must(template.New("").Parse(`
func Resolve{{ .EntityName }}(pkg *{{ .EntityName }}, overrides []string) {
{{.Code}}}
`))
	var codeBuilder strings.Builder
	for _, field := range fields {
		for _, name := range field.Names {
			if isOverridableField(field.Type) {
				var buf bytes.Buffer
				buf.WriteString(fmt.Sprintf("\t\tpkg.%s.Resolve(overrides)\n", name.Name))
				codeBuilder.Write(buf.Bytes())
			}
		}
	}
	params := struct {
		EntityName string
		Code       string
	}{
		EntityName: "Package",
		Code:       codeBuilder.String(),
	}
	err := contentTemplate.Execute(buf, params)
	if err != nil {
		log.Fatalf("execute template: %v", err)
	}
}
func main() {
	path := os.Getenv("GOFILE")
	if path == "" {
		log.Fatal("GOFILE must be set")
	}
	fset := token.NewFileSet()
	node, err := parser.ParseFile(fset, path, nil, parser.AllErrors)
	if err != nil {
		log.Fatalf("parsing file: %v", err)
	}
	entityName := "Package" // имя структуры, которую анализируем
	found := false
	fields := make([]*ast.Field, 0)
	// Ищем структуру с нужным именем
	for _, decl := range node.Decls {
		genDecl, ok := decl.(*ast.GenDecl)
		if !ok || genDecl.Tok != token.TYPE {
			continue
		}
		for _, spec := range genDecl.Specs {
			typeSpec := spec.(*ast.TypeSpec)
			if typeSpec.Name.Name != entityName {
				continue
			}
			structType, ok := typeSpec.Type.(*ast.StructType)
			if !ok {
				continue
			}
			fields = structType.Fields.List
			found = true
		}
	}
	if !found {
		log.Fatalf("struct %s not found", entityName)
	}
	var buf bytes.Buffer
	buf.WriteString("// DO NOT EDIT MANUALLY. This file is generated.\n")
	buf.WriteString("package alrsh")
	resolvedStructGenerator(&buf, fields)
	toResolvedFuncGenerator(&buf, fields)
	resolveFuncGenerator(&buf, fields)
	// Форматируем вывод
	formatted, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatalf("formatting: %v", err)
	}
	outPath := strings.TrimSuffix(path, ".go") + "_gen.go"
	outFile, err := os.Create(outPath)
	if err != nil {
		log.Fatalf("create file: %v", err)
	}
	_, err = outFile.Write(formatted)
	if err != nil {
		log.Fatalf("writing output: %v", err)
	}
	outFile.Close()
}
func exprToString(expr ast.Expr) string {
	if t, ok := expr.(*ast.IndexExpr); ok {
		if ident, ok := t.X.(*ast.Ident); ok && ident.Name == "OverridableField" {
			return exprToString(t.Index) // T
		}
	}
	var buf bytes.Buffer
	if err := format.Node(&buf, token.NewFileSet(), expr); err != nil {
		return ""
	}
	return buf.String()
}
func isOverridableField(expr ast.Expr) bool {
	indexExpr, ok := expr.(*ast.IndexExpr)
	if !ok {
		return false
	}
	ident, ok := indexExpr.X.(*ast.Ident)
	return ok && ident.Name == "OverridableField"
}