// Copyright (c) 2026 Alibaba Group and its affiliates

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sdk

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/dashscope/dashscope-confidential-maas/pkg/protocol"

	"github.com/gorilla/websocket"
)

const finishTaskAckTimeout = 1 * time.Second
const encryptedBackendErrorStatusPrefix = "cmaas_encrypted_error:v1:"

// buildParameters serializes ChatCompletionRequest into the final CMaaS
// parameters map. Fields tagged with omitempty are omitted by JSON encoding;
// sensitive fields are then replaced with encrypted payloads for the
// SDK -> secure-proxy hop.
func buildParameters(req *ChatCompletionRequest, sessionKey []byte) (map[string]interface{}, error) {
	data, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal parameters: %w", err)
	}
	params := make(map[string]interface{})
	if err := json.Unmarshal(data, &params); err != nil {
		return nil, fmt.Errorf("failed to unmarshal parameters: %w", err)
	}
	if len(req.Tools) > 0 {
		raw, err := json.Marshal(req.Tools)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal tools: %w", err)
		}
		encrypted, err := encryptContent(string(raw), sessionKey)
		if err != nil {
			return nil, fmt.Errorf("failed to encrypt tools: %w", err)
		}
		params["tools"] = encrypted
	}
	if len(req.ToolChoice) > 0 {
		encrypted, err := encryptContent(string(req.ToolChoice), sessionKey)
		if err != nil {
			return nil, fmt.Errorf("failed to encrypt tool_choice: %w", err)
		}
		params["tool_choice"] = encrypted
	}
	if len(req.Stop) > 0 {
		raw, err := json.Marshal(req.Stop)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal stop: %w", err)
		}
		encrypted, err := encryptContent(string(raw), sessionKey)
		if err != nil {
			return nil, fmt.Errorf("failed to encrypt stop: %w", err)
		}
		params["stop"] = encrypted
	}
	if len(req.StopTokenIDs) > 0 {
		raw, err := json.Marshal(req.StopTokenIDs)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal stop_token_ids: %w", err)
		}
		encrypted, err := encryptContent(string(raw), sessionKey)
		if err != nil {
			return nil, fmt.Errorf("failed to encrypt stop_token_ids: %w", err)
		}
		params["stop_token_ids"] = encrypted
	}
	return params, nil
}

func sendFinishTask(session *Session) error {
	finishReq := map[string]interface{}{
		"header": map[string]interface{}{
			"task_id":    session.taskID,
			"action":     "finish-task",
			"attributes": sdkHeaderAttributes(),
		},
		"payload": map[string]interface{}{},
	}

	reqJSON, err := json.Marshal(finishReq)
	if err != nil {
		return fmt.Errorf("failed to marshal finish-task request: %w", err)
	}
	if err := session.conn.WriteMessage(websocket.TextMessage, reqJSON); err != nil {
		return fmt.Errorf("failed to send finish-task request: %w", err)
	}
	return nil
}

func readFinishTaskAck(session *Session) {
	_ = session.conn.SetReadDeadline(time.Now().Add(finishTaskAckTimeout))
	defer session.conn.SetReadDeadline(time.Time{})

	var resp DashScopeMessage
	_ = session.conn.ReadJSON(&resp)
}

// encryptOutboundMessages copies the request messages and encrypts the
// session-key-protected fields: Message.Content plus every
// Message.ToolCalls[].Function.{Name,Arguments}. Role, ToolCallID,
// ToolCall.Index, ToolCall.ID and ToolCall.Type remain plaintext refs.
func encryptOutboundMessages(ctx context.Context, in []Message, sessionKey []byte) ([]Message, error) {
	out := make([]Message, len(in))
	for i, msg := range in {
		out[i] = Message{
			Role:       msg.Role,
			ToolCallID: msg.ToolCallID,
		}
		if len(msg.ContentParts) > 0 {
			parts, err := encryptContentParts(ctx, i, msg.ContentParts, sessionKey)
			if err != nil {
				return nil, err
			}
			out[i].ContentParts = parts
		} else if msg.Content != "" {
			encrypted, err := encryptContent(msg.Content, sessionKey)
			if err != nil {
				return nil, fmt.Errorf("failed to encrypt message %d content: %w", i, err)
			}
			out[i].Content = encrypted
		}
		if len(msg.ToolCalls) > 0 {
			out[i].ToolCalls = make([]ToolCall, len(msg.ToolCalls))
			for j, tc := range msg.ToolCalls {
				out[i].ToolCalls[j] = ToolCall{
					Index: tc.Index,
					ID:    tc.ID,
					Type:  tc.Type,
				}
				if tc.Function.Name != "" {
					encrypted, err := encryptContent(tc.Function.Name, sessionKey)
					if err != nil {
						return nil, fmt.Errorf("failed to encrypt message %d tool_call %d name: %w", i, j, err)
					}
					out[i].ToolCalls[j].Function.Name = encrypted
				}
				if tc.Function.Arguments != "" {
					encrypted, err := encryptContent(tc.Function.Arguments, sessionKey)
					if err != nil {
						return nil, fmt.Errorf("failed to encrypt message %d tool_call %d arguments: %w", i, j, err)
					}
					out[i].ToolCalls[j].Function.Arguments = encrypted
				}
			}
		}
	}
	return out, nil
}

func encryptContentParts(ctx context.Context, messageIndex int, in []ContentPart, sessionKey []byte) ([]ContentPart, error) {
	out := make([]ContentPart, len(in))
	for i, part := range in {
		out[i] = part
		switch part.Type {
		case "text":
			if part.Text == "" {
				continue
			}
			encrypted, err := encryptContent(part.Text, sessionKey)
			if err != nil {
				return nil, fmt.Errorf("failed to encrypt message %d content part %d text: %w", messageIndex, i, err)
			}
			out[i].Text = encrypted
		case "image_url":
			if part.ImageURL == nil || part.ImageURL.URL == "" {
				return nil, &ValidationError{Message: fmt.Sprintf("messages[%d].content[%d].image_url.url is required", messageIndex, i)}
			}
			normalized, err := normalizeImageURL(ctx, part.ImageURL.URL)
			if err != nil {
				return nil, &ValidationError{Message: fmt.Sprintf("invalid messages[%d].content[%d].image_url.url: %v", messageIndex, i, err)}
			}
			encrypted, err := encryptContent(normalized, sessionKey)
			if err != nil {
				return nil, fmt.Errorf("failed to encrypt message %d content part %d image_url.url: %w", messageIndex, i, err)
			}
			imageURL := *part.ImageURL
			imageURL.URL = encrypted
			out[i].ImageURL = &imageURL
		default:
			return nil, &ValidationError{Message: fmt.Sprintf("unsupported messages[%d].content[%d].type %q", messageIndex, i, part.Type)}
		}
	}
	return out, nil
}

// decryptToolCalls decrypts every Function.{Name,Arguments} in place. Errors short-circuit.
func decryptToolCalls(toolCalls []ToolCall, sessionKey []byte) ([]ToolCall, error) {
	if len(toolCalls) == 0 {
		return nil, nil
	}
	out := make([]ToolCall, len(toolCalls))
	for i, tc := range toolCalls {
		out[i] = tc
		if tc.Function.Name != "" {
			plaintext, err := decryptContent(tc.Function.Name, sessionKey)
			if err != nil {
				return nil, fmt.Errorf("failed to decrypt tool_call %d name: %w", i, err)
			}
			out[i].Function.Name = plaintext
		}
		if tc.Function.Arguments == "" {
			continue
		}
		plaintext, err := decryptContent(tc.Function.Arguments, sessionKey)
		if err != nil {
			return nil, fmt.Errorf("failed to decrypt tool_call %d arguments: %w", i, err)
		}
		out[i].Function.Arguments = plaintext
	}
	return out, nil
}

func isErrorResponse(resp *DashScopeMessage) bool {
	if resp.Header.Event == "task-failed" || resp.Header.ErrorName != "" {
		return true
	}
	if resp.Header.StatusCode >= 400 {
		return true
	}
	return resp.Header.StatusName != "" && resp.Header.StatusName != "Success"
}

// responseError builds an *APIError from a CMaaS error frame.
// Message preference: encrypted diagnostic metadata >
// header.error_message (DashScope legacy) > header.status_message
// (ModelStudio generic) > fallback constant.
// HTTP status comes from header.status_code, then gateway/API-server error code
// fields when status_code is absent. The OpenAI envelope shape is the caller's
// responsibility (openai-proxy).
func responseError(resp *DashScopeMessage, sessionKey []byte) error {
	statusCode := resp.Header.StatusCode
	if statusCode == 0 {
		code := resp.Header.ErrorName
		if code == "" {
			code = resp.Header.ErrorCode
		}
		if code == "" {
			code = resp.Header.StatusName
		}
		switch code {
		case "InvalidParameter":
			statusCode = 400
		case "UnprocessableEntity":
			statusCode = 422
		case "InternalError":
			statusCode = 500
		}
	}

	message := resp.Header.ErrorMessage
	if message == "" {
		message = resp.Header.StatusMessage
	}
	if plaintext, ok, err := decryptEncryptedErrorStatus(message, sessionKey); ok {
		if err != nil {
			return &APIError{StatusCode: statusCode, Message: "An internal error has occured during inference, please try again later or contact service support."}
		}
		return &APIError{StatusCode: statusCode, Message: plaintext}
	}
	if message == "" {
		message = "An internal error has occured during inference, please try again later or contact service support."
	}
	return &APIError{StatusCode: statusCode, Message: message}
}

func decryptEncryptedErrorStatus(message string, sessionKey []byte) (string, bool, error) {
	if !strings.HasPrefix(message, encryptedBackendErrorStatusPrefix) {
		return "", false, nil
	}
	encrypted := strings.TrimPrefix(message, encryptedBackendErrorStatusPrefix)
	plaintext, err := decryptContent(encrypted, sessionKey)
	return plaintext, true, err
}

func buildInferenceRequest(ctx context.Context, session *Session, model string, route modelRoute, req *ChatCompletionRequest) (*DashScopeMessage, error) {
	encryptedMessages, err := encryptOutboundMessages(ctx, req.Messages, session.sessionKey)
	if err != nil {
		return nil, err
	}
	parameters, err := buildParameters(req, session.sessionKey)
	if err != nil {
		return nil, err
	}

	return &DashScopeMessage{
		Header: DashScopeHeader{
			TaskID:     session.taskID,
			Streaming:  "duplex",
			Action:     "continue-task",
			Attributes: sdkHeaderAttributes(),
		},
		Payload: DashScopePayload{
			TaskGroup: route.taskGroup,
			Task:      route.task,
			Function:  route.function,
			Model:     model,
			Input: map[string]interface{}{
				"messages":         encryptedMessages,
				"action":           "inference",
				"protocol_version": protocol.CurrentProtocolVersion,
			},
			Parameters: parameters,
		},
	}, nil
}

func sendInferenceRequest(session *Session, inferReq *DashScopeMessage) error {
	reqJSON, err := json.Marshal(inferReq)
	if err != nil {
		return fmt.Errorf("failed to marshal request: %w", err)
	}
	if err := session.conn.WriteMessage(websocket.TextMessage, reqJSON); err != nil {
		return fmt.Errorf("failed to send inference request: %w", err)
	}
	return nil
}

func decryptResponseChoices(choices []responseChoice, sessionKey []byte) ([]ChatCompletionChoice, error) {
	decryptedChoices := make([]ChatCompletionChoice, 0, len(choices))
	for _, choice := range choices {
		var decryptedContent string
		if choice.Content != "" {
			plaintext, err := decryptContent(choice.Content, sessionKey)
			if err != nil {
				return nil, fmt.Errorf("failed to decrypt response choice %d: %w", choice.Index, err)
			}
			decryptedContent = plaintext
		}

		var decryptedReasoningContent string
		if choice.ReasoningContent != "" {
			plaintext, err := decryptContent(choice.ReasoningContent, sessionKey)
			if err != nil {
				return nil, fmt.Errorf("failed to decrypt reasoning response choice %d: %w", choice.Index, err)
			}
			decryptedReasoningContent = plaintext
		}

		var decryptedReasoning string
		if choice.Reasoning != "" {
			plaintext, err := decryptContent(choice.Reasoning, sessionKey)
			if err != nil {
				return nil, fmt.Errorf("failed to decrypt reasoning response choice %d: %w", choice.Index, err)
			}
			decryptedReasoning = plaintext
		}

		decryptedLogprobs, err := decryptLogprobs(choice.Logprobs, sessionKey)
		if err != nil {
			return nil, fmt.Errorf("failed to decrypt logprobs response choice %d: %w", choice.Index, err)
		}

		decryptedToolCalls, err := decryptToolCalls(choice.ToolCalls, sessionKey)
		if err != nil {
			return nil, fmt.Errorf("failed to decrypt tool_calls response choice %d: %w", choice.Index, err)
		}

		decryptedChoices = append(decryptedChoices, ChatCompletionChoice{
			Index:            choice.Index,
			Content:          decryptedContent,
			ReasoningContent: decryptedReasoningContent,
			Reasoning:        decryptedReasoning,
			FinishReason:     choice.FinishReason,
			Logprobs:         decryptedLogprobs,
			ToolCalls:        decryptedToolCalls,
		})
	}
	return decryptedChoices, nil
}

// performInference sends inference request and handles response (non-streaming)
func (c *Client) performInference(ctx context.Context, session *Session, req *ChatCompletionRequest) (*ChatCompletion, error) {
	inferReq, err := buildInferenceRequest(ctx, session, c.model, c.route, req)
	if err != nil {
		return nil, err
	}

	if err := sendInferenceRequest(session, inferReq); err != nil {
		return nil, err
	}

	// Read response
	var resp DashScopeMessage
	if err := session.conn.ReadJSON(&resp); err != nil {
		return nil, fmt.Errorf("failed to read response: %w", err)
	}

	// Check for errors
	if isErrorResponse(&resp) {
		return nil, responseError(&resp, session.sessionKey)
	}

	parsed, err := parseInferenceResponse(&resp)
	if err != nil {
		return nil, err
	}

	decryptedChoices, err := decryptResponseChoices(parsed.Choices, session.sessionKey)
	if err != nil {
		return nil, err
	}

	if !c.reuseSession {
		if err := sendFinishTask(session); err != nil {
			return nil, err
		}
		readFinishTaskAck(session)
	}

	completion := &ChatCompletion{
		Choices: decryptedChoices,
		Usage:   parsed.Usage,
		TaskID:  session.taskID,
	}
	if len(decryptedChoices) > 0 {
		first := decryptedChoices[0]
		completion.Content = first.Content
		completion.ReasoningContent = first.ReasoningContent
		completion.Reasoning = first.Reasoning
		completion.FinishReason = first.FinishReason
		completion.Logprobs = first.Logprobs
		completion.ToolCalls = first.ToolCalls
	}
	return completion, nil
}

// performInferenceStream sends inference request and returns streaming completion
func (c *Client) performInferenceStream(ctx context.Context, session *Session, req *ChatCompletionRequest, finishStreamChat func()) (*ChatCompletion, error) {
	// Default stream_options to include usage info if not set by caller
	inferReqSource := req
	if req.StreamOptions == nil {
		reqCopy := *req
		reqCopy.StreamOptions = &StreamOptions{IncludeUsage: true}
		inferReqSource = &reqCopy
	}

	inferReq, err := buildInferenceRequest(ctx, session, c.model, c.route, inferReqSource)
	if err != nil {
		return nil, err
	}

	if err := sendInferenceRequest(session, inferReq); err != nil {
		return nil, err
	}

	// Always allocate text + tool_call channels for backward-compatible
	// consumers (anthropic-proxy reads both). When the caller opts into
	// structured streaming, also allocate the Deltas channel.
	chunks := make(chan string, 10)
	toolChunks := make(chan ToolCallDelta, 10)
	completion := &ChatCompletion{
		Chunks:         chunks,
		ToolCallChunks: toolChunks,
		TaskID:         session.taskID,
	}
	var deltas chan StreamDelta
	if req.StructuredStream {
		deltas = make(chan StreamDelta, 10)
		completion.Deltas = deltas
	}

	// Start goroutine to read streaming responses
	go func() {
		if finishStreamChat != nil {
			defer finishStreamChat()
		}
		defer close(chunks)
		defer close(toolChunks)
		if deltas != nil {
			defer close(deltas)
		}

		for {
			var resp DashScopeMessage
			if err := session.conn.ReadJSON(&resp); err != nil {
				// Check if it's a normal close
				if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
					return
				}
				completion.Error = fmt.Errorf("failed to read streaming response: %w", err)
				return
			}

			// Check for errors
			if isErrorResponse(&resp) {
				completion.Error = responseError(&resp, session.sessionKey)
				return
			}

			// Parse output directly from json.RawMessage
			var output InferenceOutput
			if err := json.Unmarshal(resp.Payload.Output, &output); err != nil || len(output.Choices) == 0 {
				continue
			}
			if output.ProtocolVersion != 0 && output.ProtocolVersion != protocol.CurrentProtocolVersion {
				completion.Error = fmt.Errorf("unsupported protocol_version in inference response: %d", output.ProtocolVersion)
				return
			}

			for _, choice := range output.Choices {
				if choice.Delta == nil {
					continue
				}

				delta, err := decryptStreamDelta(choice, session.sessionKey)
				if err != nil {
					completion.Error = err
					return
				}
				hasToolCalls := len(choice.Delta.ToolCalls) > 0
				if !hasToolCalls && delta.Content == "" && delta.ReasoningContent == "" && delta.Reasoning == "" && (len(delta.Logprobs) == 0 || strings.TrimSpace(string(delta.Logprobs)) == "null") {
					continue
				}

				// chunks and deltas are mutually exclusive content paths:
				// - StructuredStream=true: only deltas (full structured info)
				// - StructuredStream=false: only chunks (text only)
				// Tool calls always go to toolChunks regardless.
				if deltas != nil {
					deltas <- delta
				} else if delta.Content != "" {
					chunks <- delta.Content
				}

				for i, tc := range choice.Delta.ToolCalls {
					toolCallIndex := i
					if tc.Index != nil {
						toolCallIndex = *tc.Index
					}
					tcd := ToolCallDelta{
						ChoiceIndex: choice.Index,
						Index:       toolCallIndex,
						ID:          tc.ID,
						Type:        tc.Type,
					}
					if tc.Function.Name != "" {
						decrypted, err := decryptContent(tc.Function.Name, session.sessionKey)
						if err != nil {
							completion.Error = fmt.Errorf("failed to decrypt choice %d tool_call %d name: %w", choice.Index, toolCallIndex, err)
							return
						}
						tcd.Name = decrypted
					}
					if tc.Function.Arguments != "" {
						decrypted, err := decryptContent(tc.Function.Arguments, session.sessionKey)
						if err != nil {
							completion.Error = fmt.Errorf("failed to decrypt choice %d tool_call %d arguments: %w", choice.Index, toolCallIndex, err)
							return
						}
						tcd.Arguments = decrypted
					}
					if tcd.Name == "" && tcd.Arguments == "" {
						continue
					}
					toolChunks <- tcd
				}
			}

			if allChoicesFinished(output.Choices) {
				finishChoices := make([]ChatCompletionChoice, 0, len(output.Choices))
				for _, choice := range output.Choices {
					finishLogprobs, err := decryptLogprobs(choice.Logprobs, session.sessionKey)
					if err != nil {
						completion.Error = fmt.Errorf("failed to decrypt finish logprobs for choice %d: %w", choice.Index, err)
						return
					}
					finishChoices = append(finishChoices, ChatCompletionChoice{
						Index:        choice.Index,
						FinishReason: choice.FinishReason,
						Logprobs:     finishLogprobs,
					})
				}
				completion.FinishChoices = finishChoices
				if len(finishChoices) > 0 {
					completion.FinishReason = finishChoices[0].FinishReason
					completion.FinishLogprobs = finishChoices[0].Logprobs
				}
				// Parse usage directly from json.RawMessage
				if len(resp.Payload.Usage) > 0 {
					_ = json.Unmarshal(resp.Payload.Usage, &completion.Usage)
				}
				if !c.reuseSession {
					if err := sendFinishTask(session); err != nil {
						completion.Error = err
						return
					}
					readFinishTaskAck(session)
				}
				return // Stream is complete
			}
		}
	}()

	return completion, nil
}

func allChoicesFinished(choices []InferenceChoice) bool {
	if len(choices) == 0 {
		return false
	}
	for _, choice := range choices {
		if choice.FinishReason == "" {
			return false
		}
	}
	return true
}

func parseInferenceResponse(resp *DashScopeMessage) (parsedInferenceResponse, error) {
	// Parse output directly from json.RawMessage
	var output InferenceOutput
	if err := json.Unmarshal(resp.Payload.Output, &output); err != nil {
		return parsedInferenceResponse{}, fmt.Errorf("failed to parse output: %w", err)
	}

	if output.ProtocolVersion != 0 && output.ProtocolVersion != protocol.CurrentProtocolVersion {
		return parsedInferenceResponse{}, fmt.Errorf("unsupported protocol_version in inference response: %d", output.ProtocolVersion)
	}

	if len(output.Choices) == 0 {
		return parsedInferenceResponse{}, fmt.Errorf("no choices in response")
	}

	var usage TokenUsage
	if len(resp.Payload.Usage) > 0 {
		_ = json.Unmarshal(resp.Payload.Usage, &usage)
	}

	choices := make([]responseChoice, 0, len(output.Choices))
	for _, choice := range output.Choices {
		if choice.Message == nil {
			return parsedInferenceResponse{}, fmt.Errorf("no message in choice %d", choice.Index)
		}
		choices = append(choices, responseChoice{
			Index:            choice.Index,
			Content:          choice.Message.Content,
			ReasoningContent: choice.Message.ReasoningContent,
			Reasoning:        choice.Message.Reasoning,
			FinishReason:     choice.FinishReason,
			Logprobs:         choice.Logprobs,
			ToolCalls:        choice.Message.ToolCalls,
		})
	}
	return parsedInferenceResponse{
		Usage:   usage,
		Choices: choices,
	}, nil
}

func decryptStreamDelta(choice InferenceChoice, sessionKey []byte) (StreamDelta, error) {
	delta := StreamDelta{Index: choice.Index}
	if choice.Delta.Content != "" {
		content, err := decryptContent(choice.Delta.Content, sessionKey)
		if err != nil {
			return delta, fmt.Errorf("failed to decrypt chunk: %w", err)
		}
		delta.Content = content
	}
	if choice.Delta.ReasoningContent != "" {
		reasoningContent, err := decryptContent(choice.Delta.ReasoningContent, sessionKey)
		if err != nil {
			return delta, fmt.Errorf("failed to decrypt reasoning chunk: %w", err)
		}
		delta.ReasoningContent = reasoningContent
	}
	if choice.Delta.Reasoning != "" {
		reasoning, err := decryptContent(choice.Delta.Reasoning, sessionKey)
		if err != nil {
			return delta, fmt.Errorf("failed to decrypt reasoning chunk: %w", err)
		}
		delta.Reasoning = reasoning
	}
	logprobs, err := decryptLogprobs(choice.Logprobs, sessionKey)
	if err != nil {
		return delta, fmt.Errorf("failed to decrypt logprobs chunk: %w", err)
	}
	delta.Logprobs = logprobs
	return delta, nil
}

func decryptLogprobs(raw json.RawMessage, sessionKey []byte) (json.RawMessage, error) {
	if len(raw) == 0 {
		return nil, nil
	}
	if strings.TrimSpace(string(raw)) == "null" {
		return json.RawMessage("null"), nil
	}
	var encrypted string
	if err := json.Unmarshal(raw, &encrypted); err != nil {
		return nil, fmt.Errorf("logprobs must be encrypted string or null")
	}
	decrypted, err := decryptContent(encrypted, sessionKey)
	if err != nil {
		return nil, err
	}
	decryptedJSON := json.RawMessage(decrypted)
	if !json.Valid(decryptedJSON) {
		return nil, fmt.Errorf("decrypted logprobs is not valid JSON")
	}
	return decryptedJSON, nil
}
