// jsonrpc.go 实现 ACP 用到的最小 JSON-RPC 2.0 codec。spec §9.5 选定自写最小
// 实现，理由：ACP 协议简单，无需引入完整 JSON-RPC 库。
//
// 关键设计：
//   - id 类型同时支持 number(int64) 和 string（spec §5.2 命名空间约定）
//   - DecodeMessage 返回统一 *Message，调用方按 IsRequest/IsResponse/IsNotification 分发
//   - 单条消息长度上限：DefaultMaxMessageBytes（防 OOM）
package acp

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"sync"
)

// JSONRPC 错误码（标准 + ACP 自定义）
const (
	JSONRPCParseError     = -32700
	JSONRPCInvalidRequest = -32600
	JSONRPCMethodNotFound = -32601
	JSONRPCInvalidParams  = -32602
	JSONRPCInternalError  = -32603

	// ACP 自定义（-32000 到 -32099 区间为 server 自定义）
	ACPFsPathOutsideWorktree = -32001
)

// DefaultMaxMessageBytes 单条 JSON-RPC message 最大字节数（防 OOM）。
const DefaultMaxMessageBytes = 8 * 1024 * 1024 // 8 MB

// Message 是 JSON-RPC 2.0 envelope 统一表示。Request 含 ID + Method；
// Notification 含 Method 但 ID = nil；Response 含 ID + (Result 或 Error)。
type Message struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.RawMessage `json:"id,omitempty"`
	Method  string          `json:"method,omitempty"`
	Params  json.RawMessage `json:"params,omitempty"`
	Result  json.RawMessage `json:"result,omitempty"`
	Error   *JSONRPCError   `json:"error,omitempty"`
}

// JSONRPCError 是 JSON-RPC 错误对象。
type JSONRPCError struct {
	Code    int             `json:"code"`
	Message string          `json:"message"`
	Data    json.RawMessage `json:"data,omitempty"`
}

func (e *JSONRPCError) Error() string {
	return fmt.Sprintf("jsonrpc error %d: %s", e.Code, e.Message)
}

// IsRequest reports whether m is a request (has ID + Method).
func (m *Message) IsRequest() bool {
	return m.Method != "" && len(m.ID) > 0 && m.Result == nil && m.Error == nil
}

// IsNotification reports whether m is a notification (has Method but no ID).
func (m *Message) IsNotification() bool {
	return m.Method != "" && len(m.ID) == 0
}

// IsResponse reports whether m is a response (has ID, no Method).
func (m *Message) IsResponse() bool {
	return m.Method == "" && len(m.ID) > 0
}

// IDInt64 attempts to extract a numeric ID. Returns (n, true) if it's a JSON
// number; (0, false) otherwise.
func (m *Message) IDInt64() (int64, bool) {
	if len(m.ID) == 0 {
		return 0, false
	}
	var n int64
	if err := json.Unmarshal(m.ID, &n); err == nil {
		return n, true
	}
	return 0, false
}

// IDString attempts to extract a string ID.
func (m *Message) IDString() (string, bool) {
	if len(m.ID) == 0 {
		return "", false
	}
	var s string
	if err := json.Unmarshal(m.ID, &s); err == nil {
		return s, true
	}
	return "", false
}

// Decoder reads newline-delimited JSON-RPC messages from a stream.
type Decoder struct {
	r       *bufio.Reader
	maxSize int
}

// NewDecoder constructs a Decoder. maxSize <= 0 → DefaultMaxMessageBytes.
func NewDecoder(r io.Reader, maxSize int) *Decoder {
	if maxSize <= 0 {
		maxSize = DefaultMaxMessageBytes
	}
	br := bufio.NewReaderSize(r, 64*1024)
	return &Decoder{r: br, maxSize: maxSize}
}

// DecodeMessage reads one LF-delimited JSON message.
func (d *Decoder) DecodeMessage() (*Message, error) {
	line, err := d.r.ReadBytes('\n')
	if err != nil {
		if errors.Is(err, io.EOF) && len(line) == 0 {
			return nil, io.EOF
		}
		if !errors.Is(err, io.EOF) {
			return nil, err
		}
	}
	if len(line) > d.maxSize {
		return nil, fmt.Errorf("acp.jsonrpc: message exceeds max size %d", d.maxSize)
	}
	var m Message
	if err := json.Unmarshal(line, &m); err != nil {
		return nil, fmt.Errorf("acp.jsonrpc: parse: %w", err)
	}
	if m.JSONRPC != "2.0" {
		return nil, fmt.Errorf("acp.jsonrpc: unsupported jsonrpc version %q", m.JSONRPC)
	}
	return &m, nil
}

// Encoder serializes Messages to a writer with LF separator.
type Encoder struct {
	w  io.Writer
	mu sync.Mutex
}

// NewEncoder constructs an Encoder. Writes are mutex-protected so concurrent
// callers (e.g. response handler + scheduled prompt) don't interleave bytes.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w: w}
}

// Encode marshals m as JSON and writes it followed by '\n'.
func (e *Encoder) Encode(m *Message) error {
	if m.JSONRPC == "" {
		m.JSONRPC = "2.0"
	}
	b, err := json.Marshal(m)
	if err != nil {
		return fmt.Errorf("acp.jsonrpc: marshal: %w", err)
	}
	e.mu.Lock()
	defer e.mu.Unlock()
	if _, err := e.w.Write(b); err != nil {
		return err
	}
	if _, err := e.w.Write([]byte{'\n'}); err != nil {
		return err
	}
	return nil
}

// NewRequestInt builds a Message with an integer id (server-initiated calls).
func NewRequestInt(id int64, method string, params any) (*Message, error) {
	idJSON, err := json.Marshal(id)
	if err != nil {
		return nil, err
	}
	pb, err := json.Marshal(params)
	if err != nil {
		return nil, err
	}
	return &Message{JSONRPC: "2.0", ID: idJSON, Method: method, Params: pb}, nil
}

// NewNotification builds a Message without id.
func NewNotification(method string, params any) (*Message, error) {
	pb, err := json.Marshal(params)
	if err != nil {
		return nil, err
	}
	return &Message{JSONRPC: "2.0", Method: method, Params: pb}, nil
}

// NewResponseOK builds a success response.
func NewResponseOK(id json.RawMessage, result any) (*Message, error) {
	rb, err := json.Marshal(result)
	if err != nil {
		return nil, err
	}
	return &Message{JSONRPC: "2.0", ID: id, Result: rb}, nil
}

// NewResponseErr builds an error response.
func NewResponseErr(id json.RawMessage, code int, msg string) *Message {
	return &Message{JSONRPC: "2.0", ID: id, Error: &JSONRPCError{Code: code, Message: msg}}
}
