package rest

import (
	"context"
	"fmt"
	"strings"

	"github.com/nexuscrm/backend/internal/application/services"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

const customFieldSuffix = "_" + "_" + "c"

func enrichCampaignMemberRecords(
	ctx context.Context,
	svcMgr *services.ServiceManager,
	records []models.SObject,
	user *models.UserSession,
) {
	if svcMgr == nil || len(records) == 0 {
		return
	}

	campaignIDs := collectReferenceIDs(records, "CampaignId")
	leadIDs := collectReferenceIDs(records, "LeadId")
	contactIDs := collectReferenceIDs(records, "ContactId")

	campaigns := retrieveManyByIDs(ctx, svcMgr, "Campaign", campaignIDs, user)
	leads := retrieveManyByIDs(ctx, svcMgr, "Lead", leadIDs, user)
	contacts := retrieveManyByIDs(ctx, svcMgr, "Contact", contactIDs, user)
	statuses := loadCampaignMemberStatuses(ctx, svcMgr, campaignIDs, user)

	for _, record := range records {
		if record == nil {
			continue
		}

		campaignID := recordStringValue(record, "CampaignId")
		leadID := recordStringValue(record, "LeadId")
		contactID := recordStringValue(record, "ContactId")

		campaign := campaigns[campaignID]
		lead := leads[leadID]
		contact := contacts[contactID]

		person := contact
		personObject := "Contact"
		if person == nil {
			person = lead
			personObject = "Lead"
		}

		lookupRecords := map[string]models.SObject{
			"Campaign": campaign,
			"Lead":     lead,
			"Contact":  contact,
		}
		fillRelatedFormulaFields(record, lookupRecords)

		if isBlank(record["Name"]) {
			if name := compoundDisplayName(person); name != "" {
				record["Name"] = name
			}
		}
		if isBlank(record["FirstName"]) {
			if value := recordStringValue(person, "FirstName"); value != "" {
				record["FirstName"] = value
			}
		}
		if isBlank(record["LastName"]) {
			if value := recordStringValue(person, "LastName"); value != "" {
				record["LastName"] = value
			}
		}
		if isBlank(record["Email"]) {
			if value := recordStringValue(person, "Email"); value != "" {
				record["Email"] = value
			}
		}
		if isBlank(record["Phone"]) {
			for _, fieldName := range []string{"Phone", "MobilePhone"} {
				if value := recordStringValue(person, fieldName); value != "" {
					record["Phone"] = value
					break
				}
			}
		}
		if isBlank(record["CompanyOrAccount"]) {
			switch {
			case personObject == "Lead":
				if value := recordStringValue(person, "Company"); value != "" {
					record["CompanyOrAccount"] = value
				}
			case personObject == "Contact":
				if value := recordStringValue(person, "AccountId_Name"); value != "" {
					record["CompanyOrAccount"] = value
				}
			}
		}
		if isBlank(record["CampaignId_Name"]) && campaign != nil {
			if value := recordStringValue(campaign, constants.FieldName); value != "" {
				record["CampaignId_Name"] = value
			}
		}

		statusLabel := recordStringValue(record, "Status")
		if campaignStatus := statuses[campaignID]; campaignStatus != nil && statusLabel != "" {
			if hasResponded, ok := campaignStatus[strings.ToLower(statusLabel)]; ok {
				record["HasResponded"] = hasResponded
			}
		}
	}
}

func collectReferenceIDs(records []models.SObject, fieldName string) []string {
	seen := make(map[string]struct{})
	ids := make([]string, 0)
	for _, record := range records {
		if record == nil {
			continue
		}
		id := recordStringValue(record, fieldName)
		if id == "" {
			continue
		}
		if _, ok := seen[id]; ok {
			continue
		}
		seen[id] = struct{}{}
		ids = append(ids, id)
	}
	return ids
}

func retrieveManyByIDs(
	ctx context.Context,
	svcMgr *services.ServiceManager,
	objectName string,
	ids []string,
	user *models.UserSession,
) map[string]models.SObject {
	if svcMgr == nil || svcMgr.Persistence == nil || len(ids) == 0 {
		return map[string]models.SObject{}
	}
	records, err := svcMgr.Persistence.RetrieveMany(ctx, objectName, ids, user)
	if err != nil || records == nil {
		return map[string]models.SObject{}
	}
	return records
}

func loadCampaignMemberStatuses(
	ctx context.Context,
	svcMgr *services.ServiceManager,
	campaignIDs []string,
	user *models.UserSession,
) map[string]map[string]bool {
	result := make(map[string]map[string]bool, len(campaignIDs))
	if svcMgr == nil || svcMgr.QuerySvc == nil {
		return result
	}

	for _, campaignID := range campaignIDs {
		filter := fmt.Sprintf("CampaignId = '%s'", strings.ReplaceAll(campaignID, "'", "''"))
		queryResult, err := svcMgr.QuerySvc.QueryWithFilter(ctx, constants.TableCampaignMemberStatus, filter, user, "SortOrder", "ASC", 500)
		if err != nil || queryResult == nil {
			continue
		}
		labels := make(map[string]bool, len(queryResult.Records))
		for _, record := range queryResult.Records {
			label := strings.ToLower(recordStringValue(record, "Label"))
			if label == "" {
				continue
			}
			hasResponded, _ := record["HasResponded"].(bool)
			labels[label] = hasResponded
		}
		result[campaignID] = labels
	}

	return result
}

func fillRelatedFormulaFields(record models.SObject, lookupRecords map[string]models.SObject) {
	if len(record) == 0 || len(lookupRecords) == 0 {
		return
	}

	for fieldName, value := range record {
		if !isBlank(value) {
			continue
		}
		prefix, sourceField, ok := splitRelatedFormulaField(fieldName)
		if !ok {
			continue
		}

		targetRecord := lookupRecords[prefix]
		if targetRecord == nil {
			continue
		}
		relatedValue, ok := targetRecord[sourceField]
		if !ok || isBlank(relatedValue) {
			continue
		}
		record[fieldName] = relatedValue
	}
}

func compoundDisplayName(record models.SObject) string {
	if record == nil {
		return ""
	}
	first := recordStringValue(record, "FirstName")
	last := recordStringValue(record, "LastName")
	if first != "" || last != "" {
		return strings.TrimSpace(first + " " + last)
	}
	return recordStringValue(record, constants.FieldName)
}

func recordStringValue(record models.SObject, fieldName string) string {
	if record == nil {
		return ""
	}
	raw, ok := record[fieldName]
	if !ok || raw == nil {
		return ""
	}
	value, ok := raw.(string)
	if !ok {
		return ""
	}
	return strings.TrimSpace(value)
}

func isBlank(value interface{}) bool {
	switch typed := value.(type) {
	case nil:
		return true
	case string:
		return strings.TrimSpace(typed) == ""
	}
	return false
}

func splitRelatedFormulaField(fieldName string) (string, string, bool) {
	if !strings.HasSuffix(fieldName, customFieldSuffix) {
		return "", "", false
	}
	base := strings.TrimSuffix(fieldName, customFieldSuffix)
	separator := strings.Index(base, "_")
	if separator <= 0 || separator == len(base)-1 {
		return "", "", false
	}
	return base[:separator], base[separator+1:], true
}
