package automation

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	apperrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/shared/pkg/models"
)

type nativeEmailTemplateContext struct {
	Record                  models.SObject
	User                    *models.UserSession
	Variables               map[string]interface{}
	checkRecordFieldVisible func(fieldAPIName string) (bool, error)
}

type nativeEmailTemplateVisibilityError struct {
	cause error
}

func (e *nativeEmailTemplateVisibilityError) Error() string {
	return fmt.Sprintf("record-field visibility lookup failed: %v", e.cause)
}

func (e *nativeEmailTemplateVisibilityError) Unwrap() error {
	return e.cause
}

func nativeEmailTemplateContextFromAction(actionCtx *ActionContext) nativeEmailTemplateContext {
	if actionCtx == nil {
		return nativeEmailTemplateContext{}
	}
	return nativeEmailTemplateContext{
		Record:    actionCtx.Record,
		User:      actionCtx.User,
		Variables: actionCtx.Variables,
	}
}

func renderNativeEmailTemplateField(fieldName, text string, ctx nativeEmailTemplateContext) (string, error) {
	rendered, err := renderNativeEmailTemplateText(text, ctx)
	if err != nil {
		var visibilityErr *nativeEmailTemplateVisibilityError
		if errors.As(err, &visibilityErr) {
			return "", err
		}
		return "", apperrors.NewValidationError("emailTemplate", fmt.Sprintf("%s: %v", fieldName, err))
	}
	return rendered, nil
}

func renderNativeEmailTemplateText(text string, ctx nativeEmailTemplateContext) (string, error) {
	if text == "" {
		return "", nil
	}
	if strings.Contains(text, "{!") {
		return "", fmt.Errorf("Salesforce merge syntax is not supported; use {{Record.*}}, {{User.*}}, or {{Variables.*}}")
	}

	var result strings.Builder
	remaining := text
	for {
		start := strings.Index(remaining, "{{")
		closeBeforeStart := strings.Index(remaining, "}}")
		if closeBeforeStart != -1 && (start == -1 || closeBeforeStart < start) {
			return "", fmt.Errorf("malformed double-brace token")
		}
		if start == -1 {
			result.WriteString(remaining)
			break
		}

		result.WriteString(remaining[:start])
		tokenStart := start + len("{{")
		endOffset := strings.Index(remaining[tokenStart:], "}}")
		if endOffset == -1 {
			return "", fmt.Errorf("malformed double-brace token")
		}

		rawToken := remaining[tokenStart : tokenStart+endOffset]
		token := strings.TrimSpace(rawToken)
		if token == "" {
			return "", fmt.Errorf("empty merge token")
		}
		if strings.ContainsAny(token, "{}!") {
			return "", fmt.Errorf("malformed merge token %q", token)
		}

		value, err := resolveNativeEmailTemplateToken(token, ctx)
		if err != nil {
			return "", err
		}
		result.WriteString(formatNativeEmailTemplateValue(value))
		remaining = remaining[tokenStart+endOffset+len("}}"):]
	}

	return result.String(), nil
}

func resolveNativeEmailTemplateToken(token string, ctx nativeEmailTemplateContext) (interface{}, error) {
	parts := strings.Split(token, ".")
	if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
		return nil, fmt.Errorf("merge token %q must use exactly one approved root and field name", token)
	}

	root := parts[0]
	path := parts[1:]
	switch root {
	case "Record":
		if ctx.checkRecordFieldVisible != nil {
			visible, err := ctx.checkRecordFieldVisible(path[0])
			if err != nil {
				return nil, &nativeEmailTemplateVisibilityError{cause: err}
			}
			if !visible {
				return nil, fmt.Errorf("unresolved merge token {{%s}}", token)
			}
		}
		value, ok := lookupNativeEmailTemplatePath(ctx.Record, path)
		if !ok {
			return nil, fmt.Errorf("unresolved merge token {{%s}}", token)
		}
		return nativeEmailTemplateScalarValue(token, value)
	case "User":
		if ctx.User == nil {
			return nil, fmt.Errorf("unresolved merge token {{%s}}", token)
		}
		value, ok := lookupNativeEmailTemplatePath(userSessionTemplateProjection(ctx.User), path)
		if !ok {
			return nil, fmt.Errorf("unresolved merge token {{%s}}", token)
		}
		return nativeEmailTemplateScalarValue(token, value)
	case "Variables":
		value, ok := lookupNativeEmailTemplatePath(ctx.Variables, path)
		if !ok {
			return nil, fmt.Errorf("unresolved merge token {{%s}}", token)
		}
		return nativeEmailTemplateScalarValue(token, value)
	default:
		return nil, fmt.Errorf("unsupported merge root %q in token {{%s}}", root, token)
	}
}

func userSessionTemplateProjection(user *models.UserSession) map[string]interface{} {
	if user == nil {
		return nil
	}
	projected := map[string]interface{}{
		"Id":        user.ID,
		"ID":        user.ID,
		"Name":      user.Name,
		"Email":     user.Email,
		"ProfileId": user.ProfileID,
		"ProfileID": user.ProfileID,
	}
	if user.RoleID != nil {
		projected["RoleId"] = user.RoleID
		projected["RoleID"] = user.RoleID
	}
	if user.RoleName != nil {
		projected["RoleName"] = user.RoleName
	}
	return projected
}

func lookupNativeEmailTemplatePath(root interface{}, path []string) (interface{}, bool) {
	if len(path) == 0 {
		return root, !isNilNativeTemplateValue(root)
	}

	current := root
	for _, part := range path {
		if part == "" || isNilNativeTemplateValue(current) {
			return nil, false
		}
		next, ok := lookupNativeEmailTemplateSegment(current, part)
		if !ok {
			return nil, false
		}
		current = next
	}
	if isNilNativeTemplateValue(current) {
		return nil, false
	}
	return current, true
}

func lookupNativeEmailTemplateSegment(value interface{}, segment string) (interface{}, bool) {
	rv := reflect.ValueOf(value)
	for rv.Kind() == reflect.Interface || rv.Kind() == reflect.Pointer {
		if rv.IsNil() {
			return nil, false
		}
		rv = rv.Elem()
	}

	switch rv.Kind() {
	case reflect.Map:
		if rv.Type().Key().Kind() != reflect.String {
			return nil, false
		}
		key := reflect.ValueOf(segment).Convert(rv.Type().Key())
		item := rv.MapIndex(key)
		if !item.IsValid() {
			return nil, false
		}
		return item.Interface(), true
	case reflect.Struct:
		field := rv.FieldByName(segment)
		if !field.IsValid() && strings.HasSuffix(segment, "Id") {
			field = rv.FieldByName(strings.TrimSuffix(segment, "Id") + "ID")
		}
		if !field.IsValid() || !field.CanInterface() {
			return nil, false
		}
		return field.Interface(), true
	default:
		return nil, false
	}
}

func isNilNativeTemplateValue(value interface{}) bool {
	if value == nil {
		return true
	}
	rv := reflect.ValueOf(value)
	switch rv.Kind() {
	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
		return rv.IsNil()
	default:
		return false
	}
}

func nativeEmailTemplateScalarValue(token string, value interface{}) (interface{}, error) {
	if !isNativeEmailTemplateScalarValue(value) {
		return nil, fmt.Errorf("unresolved merge token {{%s}}", token)
	}
	return value, nil
}

func isNativeEmailTemplateScalarValue(value interface{}) bool {
	if isNilNativeTemplateValue(value) {
		return false
	}
	rv := reflect.ValueOf(value)
	for rv.Kind() == reflect.Interface || rv.Kind() == reflect.Pointer {
		if rv.IsNil() {
			return false
		}
		rv = rv.Elem()
	}
	switch rv.Kind() {
	case reflect.Bool,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64,
		reflect.String:
		return true
	default:
		return false
	}
}

func formatNativeEmailTemplateValue(value interface{}) string {
	rv := reflect.ValueOf(value)
	for rv.Kind() == reflect.Interface || rv.Kind() == reflect.Pointer {
		if rv.IsNil() {
			return ""
		}
		rv = rv.Elem()
	}
	return fmt.Sprintf("%v", rv.Interface())
}
