package dialectsfcrm

import (
	"encoding/json"
	"strings"

	"github.com/nexuscrm/shared/pkg/models"
)

type reportRequestAdapter struct{}

func NewReportRequestAdapter() reportRequestAdapter {
	return reportRequestAdapter{}
}

type reportDefinitionRequest struct {
	Definition    models.ReportDefinition
	DeveloperName string
	FolderID      string
	Description   string
	Fields        reportDefinitionRequestFields
	Messages      reportRequestValidationMessages
}

type reportDefinitionRequestFields struct {
	Name               bool
	DeveloperName      bool
	FolderID           bool
	Description        bool
	SourceObject       bool
	SourceRef          bool
	DisplayMode        bool
	FilterLogic        bool
	DateFilter         bool
	ReportType         bool
	ReportFormat       bool
	Scope              bool
	Columns            bool
	Filters            bool
	Grouping           bool
	StandardDateFilter bool
}

type reportExecutionRequest struct {
	ReportID   string
	Definition *reportDefinitionRequest
	Limit      int
	Messages   reportRequestValidationMessages
}

type reportRequestValidationMessages struct {
	MissingName                     string
	MissingSourceObject             string
	MissingReportTypeOrSourceObject string
	MissingReportIDOrDefinition     string
}

type salesforceReportMetadata struct {
	ID                 string                            `json:"id,omitempty"`
	Name               string                            `json:"name"`
	DeveloperName      string                            `json:"developerName,omitempty"`
	FolderID           string                            `json:"folderId,omitempty"`
	Description        string                            `json:"description,omitempty"`
	SourceObject       string                            `json:"sourceObject,omitempty"`
	ReportType         interface{}                       `json:"reportType,omitempty"`
	ReportFormat       string                            `json:"reportFormat,omitempty"`
	Scope              string                            `json:"scope,omitempty"`
	DetailColumns      []string                          `json:"detailColumns,omitempty"`
	ReportFilters      []salesforceReportFilterWire      `json:"reportFilters,omitempty"`
	GroupingsDown      []map[string]interface{}          `json:"groupingsDown,omitempty"`
	StandardDateFilter *salesforceStandardDateFilterWire `json:"standardDateFilter,omitempty"`
}

type salesforceReportMetadataEnvelope struct {
	ReportMetadata salesforceReportMetadata `json:"reportMetadata"`
}

type salesforceReportExecutionRequest struct {
	ReportID       string                    `json:"reportId,omitempty"`
	ReportMetadata *salesforceReportMetadata `json:"reportMetadata,omitempty"`
	Limit          int                       `json:"limit,omitempty"`
}

func (reportRequestAdapter) ParseReportDefinitionRequest(payload []byte) (reportDefinitionRequest, error) {
	var envelope salesforceReportMetadataEnvelope
	if err := json.Unmarshal(payload, &envelope); err != nil {
		return reportDefinitionRequest{}, err
	}
	return salesforceReportDefinitionRequest(envelope.ReportMetadata), nil
}

func (reportRequestAdapter) ParseReportExecutionRequest(payload []byte) (reportExecutionRequest, error) {
	var req salesforceReportExecutionRequest
	if err := json.Unmarshal(payload, &req); err != nil {
		return reportExecutionRequest{}, err
	}

	result := reportExecutionRequest{
		ReportID: req.ReportID,
		Limit:    req.Limit,
		Messages: salesforceReportRequestMessages(),
	}
	if req.ReportMetadata != nil {
		defReq := salesforceReportDefinitionRequest(*req.ReportMetadata)
		result.Definition = &defReq
	}
	return result, nil
}

func salesforceReportDefinitionRequest(meta salesforceReportMetadata) reportDefinitionRequest {
	reportType := extractSalesforceReportType(meta.ReportType)
	sourceObject := meta.SourceObject
	if sourceObject == "" {
		sourceObject = salesforceSourceObjectFromReportType(reportType)
	}
	if reportType == "" && sourceObject != "" {
		reportType = sourceObject + "List"
	}

	def := models.ReportDefinition{
		ID:           meta.ID,
		Name:         meta.Name,
		SourceObject: sourceObject,
		SourceRef:    reportType,
		DisplayMode:  meta.ReportFormat,
		Scope:        meta.Scope,
		Columns:      append([]string(nil), meta.DetailColumns...),
		Filters:      salesforceReportFiltersToModel(meta.ReportFilters),
	}
	def.DateFilter = salesforceStandardDateFilterToModel(meta.StandardDateFilter)
	if len(meta.GroupingsDown) > 0 {
		def.Grouping = extractSalesforceGroupingNames(meta.GroupingsDown)
	}
	def.NormalizeBoundaryAliases()

	return reportDefinitionRequest{
		Definition:    def,
		DeveloperName: meta.DeveloperName,
		FolderID:      meta.FolderID,
		Description:   meta.Description,
		Messages:      salesforceReportRequestMessages(),
		Fields: reportDefinitionRequestFields{
			Name:          meta.Name != "",
			DeveloperName: meta.DeveloperName != "",
			FolderID:      meta.FolderID != "",
			Description:   meta.Description != "",
			SourceObject:  meta.SourceObject != "",
			SourceRef:     meta.ReportType != nil,
			DisplayMode:   meta.ReportFormat != "",
			DateFilter:    meta.StandardDateFilter != nil,
			ReportType:    meta.ReportType != nil,
			Scope:         meta.Scope != "",
			Columns:       meta.DetailColumns != nil,
			Filters:       meta.ReportFilters != nil,
			Grouping:      meta.GroupingsDown != nil,
		},
	}
}

func salesforceReportRequestMessages() reportRequestValidationMessages {
	return reportRequestValidationMessages{
		MissingName:                     "reportMetadata.name is required",
		MissingSourceObject:             "reportMetadata.sourceObject is required",
		MissingReportTypeOrSourceObject: "reportMetadata.reportType or reportMetadata.sourceObject is required",
		MissingReportIDOrDefinition:     "reportId or reportMetadata is required",
	}
}

func extractSalesforceReportType(v interface{}) string {
	switch t := v.(type) {
	case string:
		return t
	case map[string]interface{}:
		if s, ok := t["type"].(string); ok {
			return s
		}
	}
	return ""
}

func salesforceSourceObjectFromReportType(reportType string) string {
	sourceObject := strings.TrimSuffix(reportType, "List")
	sourceObject = strings.TrimSuffix(sourceObject, "Report")
	return sourceObject
}

func extractSalesforceGroupingNames(groupings []map[string]interface{}) []string {
	names := make([]string, 0, len(groupings))
	for _, grouping := range groupings {
		if name, ok := grouping["name"].(string); ok && name != "" {
			names = append(names, name)
		}
	}
	return names
}
