package asset_allocation_record

import (
	"context"
	"encoding/json"

	"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"
	"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"
	"gorm.io/gorm"
)

type Service struct {
	db                         *gorm.DB
	assetAllocationRecordStore *postgres.AssetAllocationRecordStore
	shopStore                  *postgres.ShopStore
	accountStore               *postgres.AccountStore
}

func New(
	db *gorm.DB,
	assetAllocationRecordStore *postgres.AssetAllocationRecordStore,
	shopStore *postgres.ShopStore,
	accountStore *postgres.AccountStore,
) *Service {
	return &Service{
		db:                         db,
		assetAllocationRecordStore: assetAllocationRecordStore,
		shopStore:                  shopStore,
		accountStore:               accountStore,
	}
}

func (s *Service) List(ctx context.Context, req *dto.ListAssetAllocationRecordRequest, userShopID *uint) (*dto.ListAssetAllocationRecordResponse, error) {
	page := req.Page
	pageSize := req.PageSize
	if page == 0 {
		page = 1
	}
	if pageSize == 0 {
		pageSize = constants.DefaultPageSize
	}

	opts := &store.QueryOptions{
		Page:     page,
		PageSize: pageSize,
	}

	filters := make(map[string]any)
	if req.AllocationType != "" {
		filters["allocation_type"] = req.AllocationType
	}
	if req.AssetType != "" {
		filters["asset_type"] = req.AssetType
	}
	if req.AssetIdentifier != "" {
		filters["asset_identifier"] = req.AssetIdentifier
	}
	if req.AllocationNo != "" {
		filters["allocation_no"] = req.AllocationNo
	}
	if req.FromShopID != nil {
		filters["from_shop_id"] = *req.FromShopID
	}
	if req.ToShopID != nil {
		filters["to_shop_id"] = *req.ToShopID
	}
	if req.OperatorID != nil {
		filters["operator_id"] = *req.OperatorID
	}
	if req.CreatedAtStart != nil {
		filters["created_at_start"] = *req.CreatedAtStart
	}
	if req.CreatedAtEnd != nil {
		filters["created_at_end"] = *req.CreatedAtEnd
	}

	if userShopID != nil {
		subordinateIDs, err := s.shopStore.GetSubordinateShopIDs(ctx, *userShopID)
		if err != nil {
			return nil, err
		}
		filters["related_shop_ids"] = subordinateIDs
	}

	records, total, err := s.assetAllocationRecordStore.List(ctx, opts, filters)
	if err != nil {
		return nil, err
	}

	shopIDs, operatorIDs := s.collectRelatedIDs(records)
	shopMap := s.loadShopNames(ctx, shopIDs)
	operatorMap := s.loadOperatorNames(ctx, operatorIDs)

	list := make([]*dto.AssetAllocationRecordResponse, 0, len(records))
	for _, record := range records {
		item := s.toResponse(record, shopMap, operatorMap)
		list = append(list, item)
	}

	return &dto.ListAssetAllocationRecordResponse{
		List:     list,
		Total:    total,
		Page:     page,
		PageSize: pageSize,
	}, nil
}

func (s *Service) GetByID(ctx context.Context, id uint) (*dto.AssetAllocationRecordDetailResponse, error) {
	record, err := s.assetAllocationRecordStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.ErrAssetAllocationRecordNotFound
		}
		return nil, err
	}

	shopIDs, operatorIDs := s.collectRelatedIDs([]*model.AssetAllocationRecord{record})
	shopMap := s.loadShopNames(ctx, shopIDs)
	operatorMap := s.loadOperatorNames(ctx, operatorIDs)

	resp := s.toResponse(record, shopMap, operatorMap)
	detail := &dto.AssetAllocationRecordDetailResponse{
		AssetAllocationRecordResponse: *resp,
	}

	if record.RelatedCardIDs != nil {
		var cardIDs []uint
		if err := json.Unmarshal(record.RelatedCardIDs, &cardIDs); err == nil {
			detail.RelatedCardIDs = cardIDs
		}
	}

	return detail, nil
}

func (s *Service) collectRelatedIDs(records []*model.AssetAllocationRecord) ([]uint, []uint) {
	shopIDSet := make(map[uint]bool)
	operatorIDSet := make(map[uint]bool)

	for _, record := range records {
		if record.FromOwnerType == constants.OwnerTypeShop && record.FromOwnerID != nil {
			shopIDSet[*record.FromOwnerID] = true
		}
		if record.ToOwnerType == constants.OwnerTypeShop {
			shopIDSet[record.ToOwnerID] = true
		}
		operatorIDSet[record.OperatorID] = true
	}

	shopIDs := make([]uint, 0, len(shopIDSet))
	for id := range shopIDSet {
		shopIDs = append(shopIDs, id)
	}

	operatorIDs := make([]uint, 0, len(operatorIDSet))
	for id := range operatorIDSet {
		operatorIDs = append(operatorIDs, id)
	}

	return shopIDs, operatorIDs
}

func (s *Service) loadShopNames(ctx context.Context, shopIDs []uint) map[uint]string {
	result := make(map[uint]string)
	if len(shopIDs) == 0 {
		return result
	}

	var shops []model.Shop
	// 使用 Unscoped() 包含已删除的店铺，确保能显示店铺名称
	s.db.WithContext(ctx).Unscoped().Where("id IN ?", shopIDs).Find(&shops)
	for _, shop := range shops {
		result[shop.ID] = shop.ShopName
	}
	return result
}

func (s *Service) loadOperatorNames(ctx context.Context, operatorIDs []uint) map[uint]string {
	result := make(map[uint]string)
	if len(operatorIDs) == 0 {
		return result
	}

	var accounts []model.Account
	s.db.WithContext(ctx).Where("id IN ?", operatorIDs).Find(&accounts)
	for _, account := range accounts {
		result[account.ID] = account.Username
	}
	return result
}

func (s *Service) toResponse(record *model.AssetAllocationRecord, shopMap map[uint]string, operatorMap map[uint]string) *dto.AssetAllocationRecordResponse {
	resp := &dto.AssetAllocationRecordResponse{
		ID:              record.ID,
		AllocationNo:    record.AllocationNo,
		AllocationType:  record.AllocationType,
		AssetType:       record.AssetType,
		AssetID:         record.AssetID,
		AssetIdentifier: record.AssetIdentifier,
		FromOwnerType:   record.FromOwnerType,
		FromOwnerID:     record.FromOwnerID,
		ToOwnerType:     record.ToOwnerType,
		ToOwnerID:       record.ToOwnerID,
		OperatorID:      record.OperatorID,
		Remark:          record.Remark,
		RelatedDeviceID: record.RelatedDeviceID,
		CreatedAt:       record.CreatedAt,
	}

	if record.AllocationType == constants.AssetAllocationTypeAllocate {
		resp.AllocationName = "分配"
	} else {
		resp.AllocationName = "回收"
	}

	if record.AssetType == constants.AssetTypeIotCard {
		resp.AssetTypeName = "物联网卡"
	} else {
		resp.AssetTypeName = "设备"
	}

	if record.FromOwnerType == constants.OwnerTypePlatform {
		resp.FromOwnerName = "平台"
	} else if record.FromOwnerID != nil {
		resp.FromOwnerName = shopMap[*record.FromOwnerID]
	}

	if record.ToOwnerType == constants.OwnerTypePlatform {
		resp.ToOwnerName = "平台"
	} else {
		resp.ToOwnerName = shopMap[record.ToOwnerID]
	}

	resp.OperatorName = operatorMap[record.OperatorID]

	if record.RelatedCardIDs != nil {
		var cardIDs []uint
		if err := json.Unmarshal(record.RelatedCardIDs, &cardIDs); err == nil {
			resp.RelatedCardCount = len(cardIDs)
		}
	}

	return resp
}
