package device

import (
	"context"
	"fmt"
	"strings"

	"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) logDeviceAudit(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.AssetTypeDevice
	}
	s.assetAuditService.LogOperation(ctx, assetAuditSvc.BuildLog(ctx, p))
}

func (s *Service) logDeviceOperation(
	ctx context.Context,
	operationType string,
	operationDesc string,
	resultStatus string,
	device *model.Device,
	beforeData map[string]any,
	afterData map[string]any,
	batchTotal int,
	successCount int,
	failCount int,
	err error,
) {
	if strings.TrimSpace(operationDesc) == "" {
		operationDesc = operationType
	}
	if strings.TrimSpace(resultStatus) == "" {
		if err != nil {
			resultStatus = constants.AssetAuditResultFailed
		} else {
			resultStatus = constants.AssetAuditResultSuccess
		}
	}

	params := assetAuditSvc.BuildLogParams{
		OperationType: operationType,
		OperationDesc: operationDesc,
		ResultStatus:  resultStatus,
		BatchTotal:    batchTotal,
		SuccessCount:  successCount,
		FailCount:     failCount,
	}
	snapshot := deviceSnapshot(device)
	beforeContent := stripDeviceOperationMeta(beforeData)
	afterContent := stripDeviceOperationMeta(afterData)
	beforeContent = ensureDeviceOperationContentMap(beforeContent, beforeData)
	afterContent = ensureDeviceOperationContentMap(afterContent, afterData)
	enrichDeviceOperationContent(beforeContent, beforeData)
	enrichDeviceOperationContent(afterContent, afterData)
	params.BeforeData, params.AfterData = assetAuditSvc.WrapOperationContent(beforeContent, afterContent, snapshot)

	if device != nil {
		params.AssetID = device.ID
		params.AssetIdentifier = device.VirtualNo
	}
	if err != nil {
		params.ErrorCode, params.ErrorMsg = assetAuditSvc.BuildErrorInfo(err)
	}

	s.logDeviceAudit(ctx, params)
}

func stripDeviceOperationMeta(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 == "devices" || k == "card" || k == "cards" || k == "asset_snapshot" || k == "operation_content" {
			continue
		}
		out[k] = v
	}
	if len(out) == 0 {
		return nil
	}
	return out
}

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

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

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

	switch devicesRaw := raw["devices"].(type) {
	case []map[string]any:
		deviceIDs, virtualNos := collectDeviceReadableLists(devicesRaw)
		if len(deviceIDs) > 0 && content["device_ids"] == nil {
			content["device_ids"] = deviceIDs
		}
		if len(virtualNos) > 0 && content["device_virtual_nos"] == nil {
			content["device_virtual_nos"] = virtualNos
		}
	case []any:
		devices := make([]map[string]any, 0, len(devicesRaw))
		for _, item := range devicesRaw {
			deviceMap, ok := item.(map[string]any)
			if !ok {
				continue
			}
			devices = append(devices, deviceMap)
		}
		deviceIDs, virtualNos := collectDeviceReadableLists(devices)
		if len(deviceIDs) > 0 && content["device_ids"] == nil {
			content["device_ids"] = deviceIDs
		}
		if len(virtualNos) > 0 && content["device_virtual_nos"] == nil {
			content["device_virtual_nos"] = virtualNos
		}
	}

	switch cardsRaw := raw["cards"].(type) {
	case []map[string]any:
		cardIDs, iccids := collectDeviceAuditCardReadableLists(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 := collectDeviceAuditCardReadableLists(cards)
		if len(cardIDs) > 0 && content["card_ids"] == nil {
			content["card_ids"] = cardIDs
		}
		if len(iccids) > 0 && content["iccids"] == nil {
			content["iccids"] = iccids
		}
	}
}

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 := stringifyDeviceAuditValue(device["virtual_no"]); v != "" {
			content["device_virtual_no"] = v
		}
	}
	if _, ok := content["device_imei"]; !ok {
		if v := stringifyDeviceAuditValue(device["imei"]); v != "" {
			content["device_imei"] = v
		}
	}
	if _, ok := content["device_sn"]; !ok {
		if v := stringifyDeviceAuditValue(device["sn"]); v != "" {
			content["device_sn"] = v
		}
	}
}

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

func collectDeviceReadableLists(devices []map[string]any) ([]any, []string) {
	deviceIDs := make([]any, 0, len(devices))
	virtualNos := make([]string, 0, len(devices))
	for _, device := range devices {
		if id, ok := device["id"]; ok {
			deviceIDs = append(deviceIDs, id)
		}
		if virtualNo := stringifyDeviceAuditValue(device["virtual_no"]); virtualNo != "" {
			virtualNos = append(virtualNos, virtualNo)
		}
	}
	return deviceIDs, virtualNos
}

func collectDeviceAuditCardReadableLists(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 := stringifyDeviceAuditValue(card["iccid"]); iccid != "" {
			iccids = append(iccids, iccid)
		}
	}
	return cardIDs, iccids
}

func stringifyDeviceAuditValue(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 deviceSnapshot(device *model.Device) map[string]any {
	if device == nil {
		return nil
	}
	return map[string]any{
		"id":              device.ID,
		"virtual_no":      device.VirtualNo,
		"imei":            device.IMEI,
		"sn":              device.SN,
		"shop_id":         device.ShopID,
		"status":          device.Status,
		"series_id":       device.SeriesID,
		"enable_polling":  device.EnablePolling,
		"realname_policy": device.RealnamePolicy,
	}
}
