package dialectsfcrm

import (
	"context"
	"encoding/json"
	"log"
	"net/http"

	"github.com/nexuscrm/dialect-sf-crm/wire"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
	"github.com/nexuscrm/shared/pkg/models"
)

const quickActionsGenericInternalErrorMessage = "An internal server error occurred. Contact your system administrator."

// QuickActionDescribe represents the Salesforce QuickAction describe format.
type QuickActionDescribe struct {
	ActionEnumOrId      string               `json:"actionEnumOrId"`
	Label               string               `json:"label"`
	Name                string               `json:"name"`
	Type                string               `json:"type"`
	TargetSobjectType   string               `json:"targetSobjectType,omitempty"`
	TargetRecordTypeId  string               `json:"targetRecordTypeId,omitempty"`
	Urls                map[string]string    `json:"urls"`
	Layout              any                  `json:"layout,omitempty"`
	VisualforcePageName string               `json:"visualforcePageName,omitempty"`
	CanvasApplicationId string               `json:"canvasApplicationId,omitempty"`
	IconUrl             string               `json:"iconUrl,omitempty"`
	Width               int                  `json:"width,omitempty"`
	Height              int                  `json:"height,omitempty"`
	DefaultValues       []QuickActionDefault `json:"defaultValues,omitempty"`
}

// QuickActionDefault represents default field values for a quick action.
type QuickActionDefault struct {
	DefaultValue any    `json:"defaultValue"`
	Field        string `json:"field"`
}

type quickActionResponse struct {
	ID            string         `json:"id"`
	ObjectAPIName string         `json:"objectApiName"`
	Name          string         `json:"name"`
	Label         string         `json:"label"`
	Type          string         `json:"type"`
	Icon          string         `json:"icon"`
	TargetObject  *string        `json:"targetObject,omitempty"`
	Config        map[string]any `json:"config,omitempty"`
}

func ListGlobalQuickActionsNeutralHandler(context.Context, *dialect.Request, dialect.Deps) (*dialect.Response, error) {
	return &dialect.Response{
		Status: http.StatusOK,
		Body:   []QuickActionDescribe{},
	}, nil
}

func ListObjectQuickActionsNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	objectName := req.Params["sobject"]
	metadata := deps.Contracts.Metadata
	policy := deps.Contracts.Policy
	if metadata == nil || policy == nil {
		return quickActionsInternalErrorResponse(), nil
	}

	if schema, err := metadata.Describe(ctx, objectName); err != nil || schema == nil {
		return &dialect.Response{
			Status: http.StatusNotFound,
			Body:   wire.SFErrorResponse(wire.SFCodeNotFound, "Object not found"),
		}, nil
	}

	access, err := policy.Access(ctx, req.User, objectName, contract.AccessRequest{})
	if err != nil || access == nil || !access.Object.Read {
		return &dialect.Response{
			Status: http.StatusForbidden,
			Body:   wire.SFErrorResponse(wire.SFCodeForbidden, "Insufficient access"),
		}, nil
	}

	actions, err := metadata.Actions(ctx, objectName)
	if err != nil {
		log.Printf("quickactions: metadata action lookup failed for %q: %v", objectName, err)
		return quickActionsInternalErrorResponse(), nil
	}

	out := make([]quickActionResponse, 0, len(actions))
	for _, action := range actions {
		if action == nil {
			continue
		}
		switch action.Name {
		case "Edit":
			if !access.Object.Update {
				continue
			}
		case "Delete":
			if !access.Object.Delete {
				continue
			}
		case "Clone":
			if !access.Object.Create {
				continue
			}
		}
		out = append(out, quickActionToResponse(action))
	}

	if out == nil {
		out = []quickActionResponse{}
	}
	return &dialect.Response{
		Status: http.StatusOK,
		Body:   map[string]any{"data": out},
	}, nil
}

func DescribeQuickActionNeutralHandler(_ context.Context, req *dialect.Request, _ dialect.Deps) (*dialect.Response, error) {
	actionName := req.Params["actionName"]
	return &dialect.Response{
		Status: http.StatusNotFound,
		Body:   wire.SFErrorResponse(wire.SFCodeNotFound, "Quick action not found: "+actionName),
	}, nil
}

func ExecuteQuickActionNeutralHandler(_ context.Context, req *dialect.Request, _ dialect.Deps) (*dialect.Response, error) {
	actionName := req.Params["actionName"]
	var body map[string]any
	if err := json.Unmarshal(req.Body, &body); err != nil {
		return &dialect.Response{
			Status: http.StatusBadRequest,
			Body:   wire.SFErrorResponse(wire.SFCodeInvalidRequest, err.Error()),
		}, nil
	}
	return &dialect.Response{
		Status: http.StatusBadRequest,
		Body:   wire.SFErrorResponse(wire.SFCodeInvalidOperation, "Quick action execution not yet implemented: "+actionName),
	}, nil
}

func ToolingQuickActionDefinitionCreateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}

	var raw map[string]any
	if err := json.Unmarshal(req.Body, &raw); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}

	action, validationResp := quickActionDefinitionFromRequest(raw)
	if validationResp != nil {
		return validationResp, nil
	}

	if err := authoring.CreateAction(ctx, &action); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}

	return jsonResponse(http.StatusCreated, map[string]any{
		"id":      action.ID,
		"success": true,
		"errors":  []any{},
	}), nil
}

func quickActionToResponse(action *models.ActionMetadata) quickActionResponse {
	return quickActionResponse{
		ID:            action.ID,
		ObjectAPIName: action.ObjectAPIName,
		Name:          action.Name,
		Label:         action.Label,
		Type:          action.Type,
		Icon:          action.Icon,
		TargetObject:  action.TargetObject,
		Config:        anyMap(action.Config),
	}
}

func quickActionsInternalErrorResponse() *dialect.Response {
	return internalErrorResponse(quickActionsGenericInternalErrorMessage)
}

func anyMap(in map[string]interface{}) map[string]any {
	if in == nil {
		return nil
	}
	out := make(map[string]any, len(in))
	for key, value := range in {
		out[key] = value
	}
	return out
}

func quickActionDefinitionFromRequest(raw map[string]any) (models.ActionMetadata, *dialect.Response) {
	if _, hasDeveloperName := raw["DeveloperName"]; hasDeveloperName {
		name := stringField(raw, "DeveloperName")
		label := stringField(raw, "MasterLabel")
		actionType := stringField(raw, "Type")
		if name == "" {
			return models.ActionMetadata{}, metadataValidationResponse("DeveloperName", "validation error on field 'DeveloperName': DeveloperName is required")
		}
		if label == "" {
			return models.ActionMetadata{}, metadataValidationResponse("MasterLabel", "validation error on field 'MasterLabel': MasterLabel is required")
		}
		if actionType == "" {
			return models.ActionMetadata{}, metadataValidationResponse("Type", "validation error on field 'Type': Type is required")
		}
		targetObject := stringField(raw, "TargetSobjectType")
		action := models.ActionMetadata{
			Name:         name,
			Label:        label,
			Type:         actionType,
			TargetObject: &targetObject,
		}
		if sobjectType := stringField(raw, "SobjectType"); sobjectType != "" && sobjectType != "Global" {
			action.ObjectAPIName = sobjectType
		}
		return action, nil
	}

	action := models.ActionMetadata{
		ID:            stringField(raw, "id"),
		ObjectAPIName: stringField(raw, "objectApiName"),
		Name:          stringField(raw, "name"),
		Label:         stringField(raw, "label"),
		Type:          stringField(raw, "type"),
		Icon:          stringField(raw, "icon"),
	}
	if targetObject := stringField(raw, "targetObject"); targetObject != "" {
		action.TargetObject = &targetObject
	}
	if config, ok := raw["config"].(map[string]any); ok {
		action.Config = config
	}
	return action, nil
}

func stringField(raw map[string]any, key string) string {
	if value, ok := raw[key].(string); ok {
		return value
	}
	return ""
}
