// Package spec defines the intermediate representation produced by the
// Telegram Bot API scraper (cmd/scrape) and consumed by the code generator
// (cmd/genapi). It is committed as internal/spec/api.json so PR diffs read
// as a Telegram changelog.
package spec

import "fmt"

// API is the top-level IR document.
type API struct {
	// Version is the Telegram Bot API version parsed from the "Recent changes" section of the docs page.
	Version string `json:"version"`
	// Types lists all object types in declaration order.
	Types []TypeDecl `json:"types"`
	// Methods lists all API methods in declaration order.
	Methods []MethodDecl `json:"methods"`
}

// TypeDecl describes a Telegram object type.
type TypeDecl struct {
	Name   string  `json:"name"`
	Doc    string  `json:"doc,omitempty"`
	Fields []Field `json:"fields,omitempty"`
	// OneOf, when non-empty, indicates this type is a union and lists the concrete variant type names.
	// Variants are emitted as concrete structs implementing a sealed interface.
	OneOf []string `json:"one_of,omitempty"`
}

// MethodDecl describes a Telegram API method.
type MethodDecl struct {
	Name    string  `json:"name"`
	Doc     string  `json:"doc,omitempty"`
	Params  []Field `json:"params,omitempty"`
	Returns TypeRef `json:"returns"`
	// HasFiles is true when any parameter accepts an InputFile, requiring a multipart/form-data request.
	HasFiles bool `json:"has_files,omitempty"`
}

// Field describes a single field on a type or a single parameter on a method.
type Field struct {
	// Name is the Go-style identifier (e.g. "ChatID").
	Name string `json:"name"`
	// JSONName is the wire name (e.g. "chat_id").
	JSONName string  `json:"json_name"`
	Type     TypeRef `json:"type"`
	Required bool    `json:"required,omitempty"`
	Doc      string  `json:"doc,omitempty"`
	// EnumValues, when non-empty, lists the wire-level string values the
	// scraper detected for an enum-like description ("can be A, B or C",
	// "always X", parse_mode special-case). Order is doc order, deduped.
	// Emitted as a typed Go enum that replaces the field's string type.
	EnumValues []string `json:"enum_values,omitempty"`
}

// Kind enumerates TypeRef shapes.
type Kind int

const (
	// KindPrimitive: int64, string, bool, float64.
	KindPrimitive Kind = iota
	// KindNamed: a TypeDecl by name.
	KindNamed
	// KindArray: ElemType is the element type.
	KindArray
	// KindOneOf: Variants lists discriminant union members.
	KindOneOf
)

// String returns a stable, lowercase representation suitable for serialisation.
func (k Kind) String() string {
	switch k {
	case KindPrimitive:
		return "primitive"
	case KindNamed:
		return "named"
	case KindArray:
		return "array"
	case KindOneOf:
		return "oneOf"
	default:
		return "unknown"
	}
}

// MarshalText / UnmarshalText keep JSON output human-readable.
func (k Kind) MarshalText() ([]byte, error) { return []byte(k.String()), nil }

func (k *Kind) UnmarshalText(b []byte) error {
	switch string(b) {
	case "primitive":
		*k = KindPrimitive
	case "named":
		*k = KindNamed
	case "array":
		*k = KindArray
	case "oneOf":
		*k = KindOneOf
	default:
		return fmt.Errorf("unknown Kind: %q", string(b))
	}
	return nil
}

// TypeRef is a structural reference used wherever a Field type is expressed.
type TypeRef struct {
	Kind     Kind     `json:"kind"`
	Name     string   `json:"name,omitempty"`
	ElemType *TypeRef `json:"elem_type,omitempty"`
	Variants []string `json:"variants,omitempty"`
}
