package enterprise_card

import (
	"context"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
	"gorm.io/gorm"
)

type Service struct {
	db                      *gorm.DB
	enterpriseStore         *postgres.EnterpriseStore
	enterpriseCardAuthStore *postgres.EnterpriseCardAuthorizationStore
	iotCardStore            *postgres.IotCardStore
}

func New(
	db *gorm.DB,
	enterpriseStore *postgres.EnterpriseStore,
	enterpriseCardAuthStore *postgres.EnterpriseCardAuthorizationStore,
	iotCardStore *postgres.IotCardStore,
) *Service {
	return &Service{
		db:                      db,
		enterpriseStore:         enterpriseStore,
		enterpriseCardAuthStore: enterpriseCardAuthStore,
		iotCardStore:            iotCardStore,
	}
}

func (s *Service) AllocateCardsPreview(ctx context.Context, enterpriseID uint, req *dto.AllocateCardsPreviewReq) (*dto.AllocateCardsPreviewResp, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
	if err != nil {
		return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
	}

	iotCardPtrs, err := s.iotCardStore.GetByICCIDs(ctx, req.ICCIDs)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询卡信息失败")
	}

	cardMap := make(map[string]*model.IotCard)
	cardIDMap := make(map[uint]*model.IotCard)
	cardIDs := make([]uint, 0, len(iotCardPtrs))
	for _, c := range iotCardPtrs {
		cardMap[c.ICCID] = c
		cardIDMap[c.ID] = c
		cardIDs = append(cardIDs, c.ID)
	}

	var bindings []model.DeviceSimBinding
	if len(cardIDs) > 0 {
		s.db.WithContext(ctx).Where("iot_card_id IN ? AND bind_status = 1", cardIDs).Find(&bindings)
	}

	cardToDevice := make(map[uint]bool)
	for _, binding := range bindings {
		cardToDevice[binding.IotCardID] = true
	}

	resp := &dto.AllocateCardsPreviewResp{
		StandaloneCards: make([]dto.StandaloneCard, 0),
		FailedItems:     make([]dto.FailedItem, 0),
	}

	for _, iccid := range req.ICCIDs {
		card, exists := cardMap[iccid]
		if !exists {
			resp.FailedItems = append(resp.FailedItems, dto.FailedItem{
				ICCID:  iccid,
				Reason: "卡不存在",
			})
			continue
		}

		if cardToDevice[card.ID] {
			resp.FailedItems = append(resp.FailedItems, dto.FailedItem{
				ICCID:  iccid,
				Reason: "该卡已绑定设备，请使用设备授权功能",
			})
			continue
		}

		resp.StandaloneCards = append(resp.StandaloneCards, dto.StandaloneCard{
			ICCID:      card.ICCID,
			IotCardID:  card.ID,
			MSISDN:     card.MSISDN,
			CarrierID:  card.CarrierID,
			StatusName: constants.GetIotCardStatusName(card.Status),
		})
	}

	resp.Summary = dto.AllocatePreviewSummary{
		StandaloneCardCount: len(resp.StandaloneCards),
		DeviceCount:         0,
		DeviceCardCount:     0,
		TotalCardCount:      len(resp.StandaloneCards),
		FailedCount:         len(resp.FailedItems),
	}

	return resp, nil
}

// resolveICCIDsForAllocate 根据选取模式解析待授权的 ICCID 列表
func (s *Service) resolveICCIDsForAllocate(ctx context.Context, req *dto.AllocateCardsReq) ([]string, error) {
	switch req.SelectionType {
	case "list":
		return req.ICCIDs, nil
	case "range":
		cards, err := s.iotCardStore.GetStandaloneByICCIDRangeForAuth(ctx, req.ICCIDStart, req.ICCIDEnd)
		if err != nil {
			return nil, errors.Wrap(errors.CodeInternalError, err, "按号段查询卡失败")
		}
		iccids := make([]string, 0, len(cards))
		for _, c := range cards {
			iccids = append(iccids, c.ICCID)
		}
		return iccids, nil
	case "filter":
		filters := map[string]any{}
		if req.ICCID != "" {
			filters["iccid"] = req.ICCID
		}
		if req.BatchNo != "" {
			filters["batch_no"] = req.BatchNo
		}
		if req.CarrierID != nil {
			filters["carrier_id"] = *req.CarrierID
		}
		if req.ShopID != nil {
			filters["shop_id"] = *req.ShopID
		}
		if len(req.ShopIDs) > 0 {
			filters["shop_ids"] = req.ShopIDs
		}
		cards, err := s.iotCardStore.GetStandaloneByAuthFilters(ctx, filters)
		if err != nil {
			return nil, errors.Wrap(errors.CodeInternalError, err, "按条件查询卡失败")
		}
		iccids := make([]string, 0, len(cards))
		for _, c := range cards {
			iccids = append(iccids, c.ICCID)
		}
		return iccids, nil
	default:
		return nil, errors.New(errors.CodeInvalidParam, "无效的选取模式")
	}
}

// resolveICCIDsForRecall 根据选取模式解析待回收的 ICCID 列表
func (s *Service) resolveICCIDsForRecall(ctx context.Context, enterpriseID uint, req *dto.RecallCardsReq) ([]string, error) {
	switch req.SelectionType {
	case "list":
		return req.ICCIDs, nil
	case "range":
		cards, err := s.iotCardStore.GetAuthorizedStandaloneByICCIDRange(ctx, enterpriseID, req.ICCIDStart, req.ICCIDEnd)
		if err != nil {
			return nil, errors.Wrap(errors.CodeInternalError, err, "按号段查询已授权卡失败")
		}
		iccids := make([]string, 0, len(cards))
		for _, c := range cards {
			iccids = append(iccids, c.ICCID)
		}
		return iccids, nil
	case "filter":
		filters := map[string]any{}
		if req.ICCID != "" {
			filters["iccid"] = req.ICCID
		}
		if req.BatchNo != "" {
			filters["batch_no"] = req.BatchNo
		}
		if req.CarrierID != nil {
			filters["carrier_id"] = *req.CarrierID
		}
		cards, err := s.iotCardStore.GetAuthorizedStandaloneByFilters(ctx, enterpriseID, filters)
		if err != nil {
			return nil, errors.Wrap(errors.CodeInternalError, err, "按条件查询已授权卡失败")
		}
		iccids := make([]string, 0, len(cards))
		for _, c := range cards {
			iccids = append(iccids, c.ICCID)
		}
		return iccids, nil
	default:
		return nil, errors.New(errors.CodeInvalidParam, "无效的选取模式")
	}
}

func (s *Service) AllocateCards(ctx context.Context, enterpriseID uint, req *dto.AllocateCardsReq) (*dto.AllocateCardsResp, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
	if err != nil {
		return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
	}

	iccids, err := s.resolveICCIDsForAllocate(ctx, req)
	if err != nil {
		return nil, err
	}

	preview, err := s.AllocateCardsPreview(ctx, enterpriseID, &dto.AllocateCardsPreviewReq{ICCIDs: iccids})
	if err != nil {
		return nil, err
	}

	resp := &dto.AllocateCardsResp{
		FailedItems: preview.FailedItems,
		FailCount:   len(preview.FailedItems),
	}

	// 构建 cardID -> ICCID 映射，用于组装失败原因
	cardIDToICCID := make(map[uint]string, len(preview.StandaloneCards))
	allCandidateIDs := make([]uint, 0, len(preview.StandaloneCards))
	for _, card := range preview.StandaloneCards {
		cardIDToICCID[card.IotCardID] = card.ICCID
		allCandidateIDs = append(allCandidateIDs, card.IotCardID)
	}

	// 检测已被其他企业授权的卡，阻止重复授权
	conflictAuths, err := s.enterpriseCardAuthStore.GetConflictingAuthsByCardIDs(ctx, enterpriseID, allCandidateIDs)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询冲突授权失败")
	}

	cardIDsToAllocate := make([]uint, 0, len(allCandidateIDs))
	for _, cardID := range allCandidateIDs {
		if _, conflict := conflictAuths[cardID]; conflict {
			resp.FailedItems = append(resp.FailedItems, dto.FailedItem{
				ICCID:  cardIDToICCID[cardID],
				Reason: "卡已授权给其他企业，请先收回",
			})
			resp.FailCount++
			continue
		}
		cardIDsToAllocate = append(cardIDsToAllocate, cardID)
	}

	existingAuths, err := s.enterpriseCardAuthStore.GetActiveAuthsByCardIDs(ctx, enterpriseID, cardIDsToAllocate)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询已有授权失败")
	}

	now := time.Now()
	userType := middleware.GetUserTypeFromContext(ctx)
	auths := make([]*model.EnterpriseCardAuthorization, 0)
	for _, cardID := range cardIDsToAllocate {
		if existingAuths[cardID] {
			continue
		}
		auths = append(auths, &model.EnterpriseCardAuthorization{
			EnterpriseID:   enterpriseID,
			CardID:         cardID,
			AuthorizedBy:   currentUserID,
			AuthorizedAt:   now,
			AuthorizerType: userType,
			Remark:         req.Remark,
		})
	}

	if len(auths) > 0 {
		if err := s.enterpriseCardAuthStore.BatchCreate(ctx, auths); err != nil {
			return nil, errors.Wrap(errors.CodeInternalError, err, "创建授权记录失败")
		}
	}

	resp.SuccessCount = len(cardIDsToAllocate)
	return resp, nil
}

func (s *Service) RecallCards(ctx context.Context, enterpriseID uint, req *dto.RecallCardsReq) (*dto.RecallCardsResp, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
	if err != nil {
		return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
	}

	iccids, err := s.resolveICCIDsForRecall(ctx, enterpriseID, req)
	if err != nil {
		return nil, err
	}

	iotCardPtrs, err := s.iotCardStore.GetByICCIDs(ctx, iccids)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询卡信息失败")
	}

	cardMap := make(map[string]*model.IotCard)
	cardIDMap := make(map[uint]*model.IotCard)
	cardIDs := make([]uint, 0, len(iotCardPtrs))
	for _, c := range iotCardPtrs {
		cardMap[c.ICCID] = c
		cardIDMap[c.ID] = c
		cardIDs = append(cardIDs, c.ID)
	}

	existingAuths, err := s.enterpriseCardAuthStore.GetActiveAuthsByCardIDs(ctx, enterpriseID, cardIDs)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询已有授权失败")
	}

	resp := &dto.RecallCardsResp{
		FailedItems:     make([]dto.FailedItem, 0),
		RecalledDevices: make([]dto.RecalledDevice, 0),
	}

	cardIDsToRecall := make([]uint, 0)
	for _, iccid := range iccids {
		card, exists := cardMap[iccid]
		if !exists {
			resp.FailedItems = append(resp.FailedItems, dto.FailedItem{
				ICCID:  iccid,
				Reason: "卡不存在",
			})
			continue
		}
		if !existingAuths[card.ID] {
			resp.FailedItems = append(resp.FailedItems, dto.FailedItem{
				ICCID:  iccid,
				Reason: "该卡未授权给此企业",
			})
			continue
		}
		cardIDsToRecall = append(cardIDsToRecall, card.ID)
	}

	if len(cardIDsToRecall) > 0 {
		if err := s.enterpriseCardAuthStore.BatchUpdateStatus(ctx, enterpriseID, cardIDsToRecall, 0); err != nil {
			return nil, errors.Wrap(errors.CodeInternalError, err, "回收授权失败")
		}
	}

	resp.SuccessCount = len(cardIDsToRecall)
	resp.FailCount = len(resp.FailedItems)
	return resp, nil
}

func (s *Service) ListCards(ctx context.Context, enterpriseID uint, req *dto.EnterpriseCardListReq) (*dto.EnterpriseCardPageResult, error) {
	_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
	if err != nil {
		return nil, errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
	}

	cardIDs, err := s.enterpriseCardAuthStore.ListCardIDsByEnterprise(ctx, enterpriseID)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询授权卡ID失败")
	}

	if len(cardIDs) == 0 {
		return &dto.EnterpriseCardPageResult{
			Items: make([]dto.EnterpriseCardItem, 0),
			Total: 0,
			Page:  req.Page,
			Size:  req.PageSize,
		}, nil
	}

	page := req.Page
	pageSize := req.PageSize
	if page == 0 {
		page = 1
	}
	if pageSize == 0 {
		pageSize = constants.DefaultPageSize
	}

	query := s.db.WithContext(ctx).Model(&model.IotCard{}).Where("id IN ?", cardIDs)

	if req.Status != nil {
		query = query.Where("status = ?", *req.Status)
	}
	if req.CarrierID != nil {
		query = query.Where("carrier_id = ?", *req.CarrierID)
	}
	if req.ICCID != "" {
		query = query.Where("iccid LIKE ?", "%"+req.ICCID+"%")
	}
	if req.VirtualNo != "" {
		query = query.Where("virtual_no LIKE ?", "%"+req.VirtualNo+"%")
	}
	if req.MSISDN != "" {
		query = query.Where("msisdn LIKE ?", "%"+req.MSISDN+"%")
	}

	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "统计卡数量失败")
	}

	var cards []model.IotCard
	offset := (page - 1) * pageSize
	if err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&cards).Error; err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询卡列表失败")
	}

	// 批量查询各卡当前生效的主套餐名称（取 package_name 快照，避免 JOIN tb_package）
	pageCardIDs := make([]uint, 0, len(cards))
	for _, card := range cards {
		pageCardIDs = append(pageCardIDs, card.ID)
	}
	packageNameMap := s.buildPackageNameMap(ctx, pageCardIDs)

	items := make([]dto.EnterpriseCardItem, 0, len(cards))
	for _, card := range cards {
		items = append(items, dto.EnterpriseCardItem{
			ID:                card.ID,
			ICCID:             card.ICCID,
			MSISDN:            card.MSISDN,
			VirtualNo:         card.VirtualNo,
			CarrierID:         card.CarrierID,
			CarrierName:       card.CarrierName,
			PackageName:       packageNameMap[card.ID],
			Status:            card.Status,
			StatusName:        constants.GetIotCardStatusName(card.Status),
			NetworkStatus:     card.NetworkStatus,
			NetworkStatusName: constants.GetNetworkStatusName(card.NetworkStatus),
			GatewayExtend:     card.GatewayExtend,
		})
	}

	return &dto.EnterpriseCardPageResult{
		Items: items,
		Total: total,
		Page:  page,
		Size:  pageSize,
	}, nil
}

// buildPackageNameMap 批量查询卡列表当前生效主套餐名称，返回 map[iotCardID]packageName
func (s *Service) buildPackageNameMap(ctx context.Context, cardIDs []uint) map[uint]string {
	result := make(map[uint]string, len(cardIDs))
	if len(cardIDs) == 0 {
		return result
	}

	type usageRow struct {
		IotCardID   uint   `gorm:"column:iot_card_id"`
		PackageName string `gorm:"column:package_name"`
	}

	// 每张卡取优先级最低（数字最小）的生效中主套餐，使用 DISTINCT ON 保证每卡仅一条
	var rows []usageRow
	s.db.WithContext(ctx).Raw(`
		SELECT DISTINCT ON (iot_card_id) iot_card_id, package_name
		FROM tb_package_usage
		WHERE iot_card_id IN ?
		  AND status = 1
		  AND master_usage_id IS NULL
		  AND deleted_at IS NULL
		ORDER BY iot_card_id, priority ASC
	`, cardIDs).Scan(&rows)

	for _, row := range rows {
		result[row.IotCardID] = row.PackageName
	}
	return result
}

func (s *Service) SuspendCard(ctx context.Context, enterpriseID, cardID uint) error {
	return s.updateCardNetworkStatus(ctx, enterpriseID, cardID, 0)
}

func (s *Service) ResumeCard(ctx context.Context, enterpriseID, cardID uint) error {
	return s.updateCardNetworkStatus(ctx, enterpriseID, cardID, 1)
}

func (s *Service) updateCardNetworkStatus(ctx context.Context, enterpriseID, cardID uint, networkStatus int) error {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	_, err := s.enterpriseStore.GetByID(ctx, enterpriseID)
	if err != nil {
		return errors.New(errors.CodeEnterpriseNotFound, "企业不存在")
	}

	auth, err := s.enterpriseCardAuthStore.GetByEnterpriseAndCard(ctx, enterpriseID, cardID)
	if err != nil || auth.RevokedAt != nil {
		return errors.New(errors.CodeForbidden, "无权限操作此卡")
	}

	return s.iotCardStore.UpdateFields(ctx, cardID, map[string]any{
		"network_status": networkStatus,
	})
}
