package device

import (
	"context"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/logger"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

func (s *Service) ListBindings(ctx context.Context, deviceID uint) (*dto.ListDeviceCardsResponse, error) {
	device, err := s.deviceStore.GetByID(ctx, deviceID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "设备不存在")
		}
		return nil, err
	}

	bindings, err := s.deviceSimBindingStore.ListByDeviceID(ctx, device.ID)
	if err != nil {
		return nil, err
	}

	cardIDs := make([]uint, 0, len(bindings))
	for _, binding := range bindings {
		cardIDs = append(cardIDs, binding.IotCardID)
	}

	var cards []*model.IotCard
	if len(cardIDs) > 0 {
		cards, err = s.iotCardStore.GetByIDs(ctx, cardIDs)
		if err != nil {
			return nil, err
		}
	}

	cardMap := make(map[uint]*model.IotCard)
	for _, card := range cards {
		cardMap[card.ID] = card
	}

	responses := make([]*dto.DeviceCardBindingResponse, 0, len(bindings))
	for _, binding := range bindings {
		card := cardMap[binding.IotCardID]
		if card == nil {
			continue
		}

		resp := &dto.DeviceCardBindingResponse{
			ID:                 binding.ID,
			SlotPosition:       binding.SlotPosition,
			IotCardID:          binding.IotCardID,
			ICCID:              card.ICCID,
			MSISDN:             card.MSISDN,
			CarrierName:        card.CarrierName,
			Status:             card.Status,
			StatusName:         constants.GetIotCardStatusName(card.Status),
			RealNameStatus:     card.RealNameStatus,
			RealNameStatusName: constants.GetRealNameStatusName(card.RealNameStatus),
			BindTime:           binding.BindTime,
			IsCurrent:          binding.IsCurrent,
			RealnamePolicy:     card.RealnamePolicy,
			NetworkStatus:      card.NetworkStatus,
			GatewayExtend:      card.GatewayExtend,
		}
		responses = append(responses, resp)
	}

	return &dto.ListDeviceCardsResponse{
		Bindings: responses,
	}, nil
}

func (s *Service) BindCard(ctx context.Context, deviceID uint, req *dto.BindCardToDeviceRequest) (*dto.BindCardToDeviceResponse, error) {
	device, err := s.deviceStore.GetByID(ctx, deviceID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			appErr := errors.New(errors.CodeNotFound, "设备不存在")
			s.logDeviceOperation(
				ctx,
				constants.AssetAuditOpDeviceBindCard,
				"设备绑卡失败",
				constants.AssetAuditResultFailed,
				nil,
				nil,
				map[string]any{
					"device_id":     deviceID,
					"iot_card_id":   req.IotCardID,
					"slot_position": req.SlotPosition,
				},
				0,
				0,
				0,
				appErr,
			)
			return nil, appErr
		}
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceBindCard,
			"设备绑卡失败",
			constants.AssetAuditResultFailed,
			nil,
			nil,
			map[string]any{
				"device_id":     deviceID,
				"iot_card_id":   req.IotCardID,
				"slot_position": req.SlotPosition,
			},
			0,
			0,
			0,
			err,
		)
		return nil, err
	}

	if req.SlotPosition > device.MaxSimSlots {
		appErr := errors.New(errors.CodeInvalidParam, "插槽位置超出设备最大插槽数")
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceBindCard,
			"设备绑卡被拒绝",
			constants.AssetAuditResultDenied,
			device,
			map[string]any{"device": deviceSnapshot(device)},
			map[string]any{
				"iot_card_id":   req.IotCardID,
				"slot_position": req.SlotPosition,
			},
			0,
			0,
			0,
			appErr,
		)
		return nil, appErr
	}

	existingBinding, err := s.deviceSimBindingStore.GetByDeviceAndSlot(ctx, device.ID, req.SlotPosition)
	if err != nil && err != gorm.ErrRecordNotFound {
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceBindCard,
			"设备绑卡失败",
			constants.AssetAuditResultFailed,
			device,
			map[string]any{"device": deviceSnapshot(device)},
			map[string]any{
				"iot_card_id":   req.IotCardID,
				"slot_position": req.SlotPosition,
			},
			0,
			0,
			0,
			err,
		)
		return nil, err
	}
	if existingBinding != nil {
		appErr := errors.New(errors.CodeConflict, "该插槽已有绑定的卡")
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceBindCard,
			"设备绑卡被拒绝",
			constants.AssetAuditResultDenied,
			device,
			map[string]any{"device": deviceSnapshot(device)},
			map[string]any{
				"iot_card_id":   req.IotCardID,
				"slot_position": req.SlotPosition,
			},
			0,
			0,
			0,
			appErr,
		)
		return nil, appErr
	}

	card, err := s.iotCardStore.GetByID(ctx, req.IotCardID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			appErr := errors.New(errors.CodeIotCardNotFound)
			s.logDeviceOperation(
				ctx,
				constants.AssetAuditOpDeviceBindCard,
				"设备绑卡失败",
				constants.AssetAuditResultFailed,
				device,
				map[string]any{"device": deviceSnapshot(device)},
				map[string]any{
					"iot_card_id":   req.IotCardID,
					"slot_position": req.SlotPosition,
				},
				0,
				0,
				0,
				appErr,
			)
			return nil, appErr
		}
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceBindCard,
			"设备绑卡失败",
			constants.AssetAuditResultFailed,
			device,
			map[string]any{"device": deviceSnapshot(device)},
			map[string]any{
				"iot_card_id":   req.IotCardID,
				"slot_position": req.SlotPosition,
			},
			0,
			0,
			0,
			err,
		)
		return nil, err
	}

	activeBinding, err := s.deviceSimBindingStore.GetActiveBindingByCardID(ctx, card.ID)
	if err != nil && err != gorm.ErrRecordNotFound {
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceBindCard,
			"设备绑卡失败",
			constants.AssetAuditResultFailed,
			device,
			map[string]any{"device": deviceSnapshot(device)},
			map[string]any{
				"iot_card_id":   req.IotCardID,
				"slot_position": req.SlotPosition,
			},
			0,
			0,
			0,
			err,
		)
		return nil, err
	}
	if activeBinding != nil {
		appErr := errors.New(errors.CodeIotCardBoundToDevice, "该卡已绑定到其他设备")
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceBindCard,
			"设备绑卡被拒绝",
			constants.AssetAuditResultDenied,
			device,
			map[string]any{
				"device": deviceSnapshot(device),
				"card": map[string]any{
					"id":     card.ID,
					"iccid":  card.ICCID,
					"status": card.Status,
				},
			},
			map[string]any{
				"iot_card_id":   req.IotCardID,
				"slot_position": req.SlotPosition,
			},
			0,
			0,
			0,
			appErr,
		)
		return nil, appErr
	}

	binding := &model.DeviceSimBinding{
		DeviceID:     device.ID,
		IotCardID:    card.ID,
		SlotPosition: req.SlotPosition,
		BindStatus:   1,
	}

	if err := s.deviceSimBindingStore.Create(ctx, binding); err != nil {
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceBindCard,
			"设备绑卡失败",
			constants.AssetAuditResultFailed,
			device,
			map[string]any{
				"device": deviceSnapshot(device),
				"card": map[string]any{
					"id":     card.ID,
					"iccid":  card.ICCID,
					"status": card.Status,
				},
			},
			map[string]any{
				"slot_position": req.SlotPosition,
			},
			0,
			0,
			0,
			err,
		)
		return nil, err
	}

	// 异步快照维护：更新卡上的设备虚拟号，失败不阻断主流程
	if updateErr := s.iotCardStore.UpdateDeviceVirtualNo(ctx, card.ID, device.VirtualNo); updateErr != nil {
		logger.GetAppLogger().Warn("更新卡的设备虚拟号快照失败",
			zap.Uint("card_id", card.ID),
			zap.Uint("device_id", device.ID),
			zap.Error(updateErr),
		)
	}

	s.logDeviceOperation(
		ctx,
		constants.AssetAuditOpDeviceBindCard,
		"设备绑卡",
		constants.AssetAuditResultSuccess,
		device,
		map[string]any{
			"device": deviceSnapshot(device),
			"card": map[string]any{
				"id":     card.ID,
				"iccid":  card.ICCID,
				"status": card.Status,
			},
		},
		map[string]any{
			"binding_id":    binding.ID,
			"slot_position": req.SlotPosition,
			"iot_card_id":   card.ID,
			"iccid":         card.ICCID,
		},
		1,
		1,
		0,
		nil,
	)

	return &dto.BindCardToDeviceResponse{
		BindingID: binding.ID,
		Message:   "绑定成功",
	}, nil
}

func (s *Service) UnbindCard(ctx context.Context, deviceID uint, cardID uint) (*dto.UnbindCardFromDeviceResponse, error) {
	device, err := s.deviceStore.GetByID(ctx, deviceID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			appErr := errors.New(errors.CodeNotFound, "设备不存在")
			s.logDeviceOperation(
				ctx,
				constants.AssetAuditOpDeviceUnbindCard,
				"设备解绑卡失败",
				constants.AssetAuditResultFailed,
				nil,
				nil,
				map[string]any{
					"device_id":   deviceID,
					"iot_card_id": cardID,
				},
				0,
				0,
				0,
				appErr,
			)
			return nil, appErr
		}
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceUnbindCard,
			"设备解绑卡失败",
			constants.AssetAuditResultFailed,
			nil,
			nil,
			map[string]any{
				"device_id":   deviceID,
				"iot_card_id": cardID,
			},
			0,
			0,
			0,
			err,
		)
		return nil, err
	}

	binding, err := s.deviceSimBindingStore.GetByDeviceAndCard(ctx, device.ID, cardID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			appErr := errors.New(errors.CodeNotFound, "该卡未绑定到此设备")
			s.logDeviceOperation(
				ctx,
				constants.AssetAuditOpDeviceUnbindCard,
				"设备解绑卡被拒绝",
				constants.AssetAuditResultDenied,
				device,
				map[string]any{"device": deviceSnapshot(device)},
				map[string]any{"iot_card_id": cardID},
				0,
				0,
				0,
				appErr,
			)
			return nil, appErr
		}
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceUnbindCard,
			"设备解绑卡失败",
			constants.AssetAuditResultFailed,
			device,
			map[string]any{"device": deviceSnapshot(device)},
			map[string]any{"iot_card_id": cardID},
			0,
			0,
			0,
			err,
		)
		return nil, err
	}

	var cardAudit map[string]any
	if card, cardErr := s.iotCardStore.GetByID(ctx, binding.IotCardID); cardErr == nil {
		cardAudit = map[string]any{
			"id":     card.ID,
			"iccid":  card.ICCID,
			"status": card.Status,
		}
	}

	beforeAuditData := map[string]any{
		"device":      deviceSnapshot(device),
		"binding_id":  binding.ID,
		"iot_card_id": binding.IotCardID,
	}
	if cardAudit != nil {
		beforeAuditData["card"] = cardAudit
	}

	if err := s.deviceSimBindingStore.Unbind(ctx, binding.ID); err != nil {
		s.logDeviceOperation(
			ctx,
			constants.AssetAuditOpDeviceUnbindCard,
			"设备解绑卡失败",
			constants.AssetAuditResultFailed,
			device,
			beforeAuditData,
			nil,
			0,
			0,
			0,
			err,
		)
		return nil, err
	}

	// 解绑后清空卡的设备虚拟号快照，失败不阻断主流程
	if updateErr := s.iotCardStore.UpdateDeviceVirtualNo(ctx, cardID, ""); updateErr != nil {
		logger.GetAppLogger().Warn("清空卡的设备虚拟号快照失败",
			zap.Uint("card_id", cardID),
			zap.Uint("device_id", deviceID),
			zap.Error(updateErr),
		)
	}

	afterAuditData := map[string]any{
		"iot_card_id": cardID,
		"unbind":      true,
	}
	if cardAudit != nil {
		afterAuditData["card"] = cardAudit
	}

	s.logDeviceOperation(
		ctx,
		constants.AssetAuditOpDeviceUnbindCard,
		"设备解绑卡",
		constants.AssetAuditResultSuccess,
		device,
		beforeAuditData,
		afterAuditData,
		1,
		1,
		0,
		nil,
	)

	return &dto.UnbindCardFromDeviceResponse{
		Message: "解绑成功",
	}, nil
}
