package iot_card

import (
	"context"
	"fmt"

	"github.com/break/junhong_cmp_fiber/internal/model"
	assetAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_audit"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
)

// AssetAuditService 资产审计服务接口。
type AssetAuditService interface {
	LogOperation(ctx context.Context, log *model.AssetOperationLog)
}

func (s *Service) logCardAudit(ctx context.Context, p assetAuditSvc.BuildLogParams) {
	if s == nil || s.assetAuditService == nil {
		return
	}
	if p.Operator.Type == "" {
		p.Operator = assetAuditSvc.OperatorFromContext(ctx)
	}
	if p.AssetType == "" {
		p.AssetType = constants.AssetTypeIotCard
	}
	p.BeforeData, p.AfterData = normalizeCardAuditPayload(p.BeforeData, p.AfterData)
	s.assetAuditService.LogOperation(ctx, assetAuditSvc.BuildLog(ctx, p))
}

func (s *StopResumeService) logCardAudit(ctx context.Context, p assetAuditSvc.BuildLogParams) {
	if s == nil || s.assetAuditService == nil {
		return
	}
	if p.Operator.Type == "" {
		p.Operator = assetAuditSvc.OperatorFromContext(ctx)
	}
	if p.AssetType == "" {
		p.AssetType = constants.AssetTypeIotCard
	}
	p.BeforeData, p.AfterData = normalizeCardAuditPayload(p.BeforeData, p.AfterData)
	s.assetAuditService.LogOperation(ctx, assetAuditSvc.BuildLog(ctx, p))
}

func normalizeCardAuditPayload(beforeData, afterData map[string]any) (map[string]any, map[string]any) {
	snapshot := map[string]any(nil)
	if raw, ok := beforeData["card"]; ok {
		if m, ok := raw.(map[string]any); ok {
			snapshot = m
		}
	}
	if snapshot == nil {
		if raw, ok := afterData["card"]; ok {
			if m, ok := raw.(map[string]any); ok {
				snapshot = m
			}
		}
	}
	beforeContent := stripCardOperationMeta(beforeData)
	afterContent := stripCardOperationMeta(afterData)
	beforeContent = ensureCardOperationContentMap(beforeContent, beforeData)
	afterContent = ensureCardOperationContentMap(afterContent, afterData)
	enrichCardOperationContent(beforeContent, beforeData)
	enrichCardOperationContent(afterContent, afterData)
	return assetAuditSvc.WrapOperationContent(beforeContent, afterContent, snapshot)
}

func stripCardOperationMeta(data map[string]any) map[string]any {
	if len(data) == 0 {
		return nil
	}
	out := make(map[string]any, len(data))
	for k, v := range data {
		if k == "device" || k == "card" || k == "cards" || k == "asset_snapshot" || k == "operation_content" {
			continue
		}
		out[k] = v
	}
	if len(out) == 0 {
		return nil
	}
	return out
}

func ensureCardOperationContentMap(content map[string]any, raw map[string]any) map[string]any {
	if content != nil || len(raw) == 0 {
		return content
	}
	if _, ok := raw["card"].(map[string]any); ok {
		return make(map[string]any)
	}
	if _, ok := raw["device"].(map[string]any); ok {
		return make(map[string]any)
	}
	switch raw["cards"].(type) {
	case []map[string]any, []any:
		return make(map[string]any)
	default:
		return content
	}
}

func enrichCardOperationContent(content map[string]any, raw map[string]any) {
	if len(raw) == 0 {
		return
	}

	if cardRaw, ok := raw["card"].(map[string]any); ok {
		mergeReadableCardFields(content, cardRaw)
	}
	if deviceRaw, ok := raw["device"].(map[string]any); ok {
		mergeReadableDeviceFields(content, deviceRaw)
	}

	switch cardsRaw := raw["cards"].(type) {
	case []map[string]any:
		cardIDs, iccids := collectCardReadableLists(cardsRaw)
		if len(cardIDs) > 0 && content["card_ids"] == nil {
			content["card_ids"] = cardIDs
		}
		if len(iccids) > 0 && content["iccids"] == nil {
			content["iccids"] = iccids
		}
	case []any:
		cards := make([]map[string]any, 0, len(cardsRaw))
		for _, item := range cardsRaw {
			cardMap, ok := item.(map[string]any)
			if !ok {
				continue
			}
			cards = append(cards, cardMap)
		}
		cardIDs, iccids := collectCardReadableLists(cards)
		if len(cardIDs) > 0 && content["card_ids"] == nil {
			content["card_ids"] = cardIDs
		}
		if len(iccids) > 0 && content["iccids"] == nil {
			content["iccids"] = iccids
		}
	}
}

func mergeReadableCardFields(content map[string]any, card map[string]any) {
	if len(card) == 0 {
		return
	}
	if _, ok := content["card_id"]; !ok {
		if v, exists := card["id"]; exists {
			content["card_id"] = v
		}
	}
	if _, ok := content["iccid"]; !ok {
		if v := stringifyCardAuditValue(card["iccid"]); v != "" {
			content["iccid"] = v
		}
	}
	if _, ok := content["device_virtual_no"]; !ok {
		if v := stringifyCardAuditValue(card["device_virtual_no"]); v != "" {
			content["device_virtual_no"] = v
		}
	}
}

func mergeReadableDeviceFields(content map[string]any, device map[string]any) {
	if len(device) == 0 {
		return
	}
	if _, ok := content["device_id"]; !ok {
		if v, exists := device["id"]; exists {
			content["device_id"] = v
		}
	}
	if _, ok := content["device_virtual_no"]; !ok {
		if v := stringifyCardAuditValue(device["virtual_no"]); v != "" {
			content["device_virtual_no"] = v
		}
	}
	if _, ok := content["device_imei"]; !ok {
		if v := stringifyCardAuditValue(device["imei"]); v != "" {
			content["device_imei"] = v
		}
	}
	if _, ok := content["device_sn"]; !ok {
		if v := stringifyCardAuditValue(device["sn"]); v != "" {
			content["device_sn"] = v
		}
	}
}

func collectCardReadableLists(cards []map[string]any) ([]any, []string) {
	cardIDs := make([]any, 0, len(cards))
	iccids := make([]string, 0, len(cards))
	for _, card := range cards {
		if id, ok := card["id"]; ok {
			cardIDs = append(cardIDs, id)
		}
		if iccid := stringifyCardAuditValue(card["iccid"]); iccid != "" {
			iccids = append(iccids, iccid)
		}
	}
	return cardIDs, iccids
}

func stringifyCardAuditValue(v any) string {
	switch vv := v.(type) {
	case string:
		return vv
	case fmt.Stringer:
		return vv.String()
	default:
		if vv == nil {
			return ""
		}
		return fmt.Sprint(vv)
	}
}

func cardSnapshot(card *model.IotCard) map[string]any {
	if card == nil {
		return nil
	}
	return map[string]any{
		"id":                card.ID,
		"iccid":             card.ICCID,
		"device_virtual_no": card.DeviceVirtualNo,
		"shop_id":           card.ShopID,
		"status":            card.Status,
		"series_id":         card.SeriesID,
		"enable_polling":    card.EnablePolling,
		"realname_policy":   card.RealnamePolicy,
		"real_name_status":  card.RealNameStatus,
		"network_status":    card.NetworkStatus,
		"stop_reason":       card.StopReason,
	}
}
