package exchange

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"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

type Service struct {
	db                           *gorm.DB
	exchangeStore                *postgres.ExchangeOrderStore
	iotCardStore                 *postgres.IotCardStore
	deviceStore                  *postgres.DeviceStore
	assetWalletStore             *postgres.AssetWalletStore
	assetWalletTransactionStore  *postgres.AssetWalletTransactionStore
	packageUsageStore            *postgres.PackageUsageStore
	packageUsageDailyRecordStore *postgres.PackageUsageDailyRecordStore
	resourceTagStore             *postgres.ResourceTagStore
	personalCustomerDeviceStore  *postgres.PersonalCustomerDeviceStore
	logger                       *zap.Logger
}

func New(
	db *gorm.DB,
	exchangeStore *postgres.ExchangeOrderStore,
	iotCardStore *postgres.IotCardStore,
	deviceStore *postgres.DeviceStore,
	assetWalletStore *postgres.AssetWalletStore,
	assetWalletTransactionStore *postgres.AssetWalletTransactionStore,
	packageUsageStore *postgres.PackageUsageStore,
	packageUsageDailyRecordStore *postgres.PackageUsageDailyRecordStore,
	resourceTagStore *postgres.ResourceTagStore,
	personalCustomerDeviceStore *postgres.PersonalCustomerDeviceStore,
	logger *zap.Logger,
) *Service {
	return &Service{
		db:                           db,
		exchangeStore:                exchangeStore,
		iotCardStore:                 iotCardStore,
		deviceStore:                  deviceStore,
		assetWalletStore:             assetWalletStore,
		assetWalletTransactionStore:  assetWalletTransactionStore,
		packageUsageStore:            packageUsageStore,
		packageUsageDailyRecordStore: packageUsageDailyRecordStore,
		resourceTagStore:             resourceTagStore,
		personalCustomerDeviceStore:  personalCustomerDeviceStore,
		logger:                       logger,
	}
}

func (s *Service) Create(ctx context.Context, req *dto.CreateExchangeRequest) (*dto.ExchangeOrderResponse, error) {
	asset, err := s.resolveAssetByIdentifier(ctx, req.OldAssetType, req.OldIdentifier)
	if err != nil {
		return nil, err
	}

	if _, err = s.exchangeStore.FindActiveByOldAsset(ctx, asset.AssetType, asset.AssetID); err == nil {
		return nil, errors.New(errors.CodeExchangeInProgress)
	} else if err != gorm.ErrRecordNotFound {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询进行中换货单失败")
	}

	shopID := middleware.GetShopIDFromContext(ctx)
	creator := middleware.GetUserIDFromContext(ctx)
	order := &model.ExchangeOrder{
		ExchangeNo:         model.GenerateExchangeNo(),
		OldAssetType:       asset.AssetType,
		OldAssetID:         asset.AssetID,
		OldAssetIdentifier: asset.Identifier,
		ExchangeReason:     req.ExchangeReason,
		Remark:             req.Remark,
		Status:             constants.ExchangeStatusPendingInfo,
		MigrationCompleted: false,
		MigrationBalance:   0,
		MigrateData:        false,
		BaseModel:          model.BaseModel{Creator: creator, Updater: creator},
	}
	if shopID > 0 {
		order.ShopID = &shopID
	}

	if err = s.exchangeStore.Create(ctx, order); err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "创建换货单失败")
	}

	return s.toExchangeOrderResponse(order), nil
}

func (s *Service) List(ctx context.Context, req *dto.ExchangeListRequest) (*dto.ExchangeListResponse, error) {
	page := req.Page
	page = max(page, 1)
	pageSize := req.PageSize
	if pageSize < 1 {
		pageSize = constants.DefaultPageSize
	}
	if pageSize > constants.MaxPageSize {
		pageSize = constants.MaxPageSize
	}

	filters := make(map[string]any)
	if req.Status != nil {
		filters["status"] = *req.Status
	}
	if req.Identifier != "" {
		filters["identifier"] = req.Identifier
	}
	if req.CreatedAtStart != nil {
		filters["created_at_start"] = *req.CreatedAtStart
	}
	if req.CreatedAtEnd != nil {
		filters["created_at_end"] = *req.CreatedAtEnd
	}

	orders, total, err := s.exchangeStore.List(ctx, filters, page, pageSize)
	if err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询换货单列表失败")
	}

	list := make([]*dto.ExchangeOrderResponse, 0, len(orders))
	for _, item := range orders {
		list = append(list, s.toExchangeOrderResponse(item))
	}

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

func (s *Service) Get(ctx context.Context, id uint) (*dto.ExchangeOrderResponse, error) {
	order, err := s.exchangeStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeExchangeOrderNotFound)
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询换货单详情失败")
	}
	return s.toExchangeOrderResponse(order), nil
}

func (s *Service) Ship(ctx context.Context, id uint, req *dto.ExchangeShipRequest) (*dto.ExchangeOrderResponse, error) {
	order, err := s.exchangeStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeExchangeOrderNotFound)
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询换货单失败")
	}
	if order.Status != constants.ExchangeStatusPendingShip {
		return nil, errors.New(errors.CodeExchangeStatusInvalid)
	}

	newAsset, err := s.resolveAssetByIdentifier(ctx, order.OldAssetType, req.NewIdentifier)
	if err != nil {
		return nil, err
	}
	if newAsset.AssetType != order.OldAssetType {
		return nil, errors.New(errors.CodeExchangeAssetTypeMismatch)
	}
	if newAsset.AssetStatus != 1 {
		return nil, errors.New(errors.CodeExchangeNewAssetNotInStock)
	}

	updates := map[string]any{
		"new_asset_type":       newAsset.AssetType,
		"new_asset_id":         newAsset.AssetID,
		"new_asset_identifier": newAsset.Identifier,
		"express_company":      req.ExpressCompany,
		"express_no":           req.ExpressNo,
		"migrate_data":         req.MigrateData,
		"updater":              middleware.GetUserIDFromContext(ctx),
		"updated_at":           time.Now(),
	}
	if err = s.exchangeStore.UpdateStatus(ctx, id, constants.ExchangeStatusPendingShip, constants.ExchangeStatusShipped, updates); err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeExchangeStatusInvalid)
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "更新换货单发货状态失败")
	}

	return s.Get(ctx, id)
}

func (s *Service) Complete(ctx context.Context, id uint) error {
	order, err := s.exchangeStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeExchangeOrderNotFound)
		}
		return errors.Wrap(errors.CodeDatabaseError, err, "查询换货单失败")
	}
	if order.Status != constants.ExchangeStatusShipped {
		return errors.New(errors.CodeExchangeStatusInvalid)
	}

	updates := map[string]any{
		"updater":    middleware.GetUserIDFromContext(ctx),
		"updated_at": time.Now(),
	}
	if order.MigrateData {
		var migrationBalance int64
		migrationBalance, err = s.executeMigration(ctx, order)
		if err != nil {
			return err
		}
		updates["migration_completed"] = true
		updates["migration_balance"] = migrationBalance
	}

	if err = s.exchangeStore.UpdateStatus(ctx, id, constants.ExchangeStatusShipped, constants.ExchangeStatusCompleted, updates); err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeExchangeStatusInvalid)
		}
		return errors.Wrap(errors.CodeDatabaseError, err, "确认换货完成失败")
	}

	return nil
}

func (s *Service) Cancel(ctx context.Context, id uint, req *dto.ExchangeCancelRequest) error {
	order, err := s.exchangeStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeExchangeOrderNotFound)
		}
		return errors.Wrap(errors.CodeDatabaseError, err, "查询换货单失败")
	}
	if order.Status != constants.ExchangeStatusPendingInfo && order.Status != constants.ExchangeStatusPendingShip {
		return errors.New(errors.CodeExchangeStatusInvalid)
	}

	updates := map[string]any{
		"updater":    middleware.GetUserIDFromContext(ctx),
		"updated_at": time.Now(),
	}
	if req != nil {
		updates["remark"] = req.Remark
	}
	if err = s.exchangeStore.UpdateStatus(ctx, id, order.Status, constants.ExchangeStatusCancelled, updates); err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeExchangeStatusInvalid)
		}
		return errors.Wrap(errors.CodeDatabaseError, err, "取消换货失败")
	}
	return nil
}

func (s *Service) Renew(ctx context.Context, id uint) error {
	order, err := s.exchangeStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeExchangeOrderNotFound)
		}
		return errors.Wrap(errors.CodeDatabaseError, err, "查询换货单失败")
	}
	if order.Status != constants.ExchangeStatusCompleted {
		return errors.New(errors.CodeExchangeStatusInvalid)
	}

	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if order.OldAssetType == constants.ExchangeAssetTypeIotCard {
			var card model.IotCard
			if err = tx.Where("id = ?", order.OldAssetID).First(&card).Error; err != nil {
				if err == gorm.ErrRecordNotFound {
					return errors.New(errors.CodeAssetNotFound)
				}
				return errors.Wrap(errors.CodeDatabaseError, err, "查询旧卡失败")
			}
			if card.AssetStatus != 3 {
				return errors.New(errors.CodeExchangeAssetNotExchanged)
			}

			if err = tx.Model(&model.IotCard{}).Where("id = ?", card.ID).Updates(map[string]any{
				"generation":                         card.Generation + 1,
				"asset_status":                       1,
				"accumulated_recharge":               0,
				"first_commission_paid":              false,
				"accumulated_recharge_by_series":     "{}",
				"first_recharge_triggered_by_series": "{}",
				"updater":                            middleware.GetUserIDFromContext(ctx),
				"updated_at":                         time.Now(),
			}).Error; err != nil {
				return errors.Wrap(errors.CodeDatabaseError, err, "重置旧卡转新状态失败")
			}

			if err = tx.Where("virtual_no = ?", card.VirtualNo).Delete(&model.PersonalCustomerDevice{}).Error; err != nil {
				return errors.Wrap(errors.CodeDatabaseError, err, "清理个人客户绑定失败")
			}

			if err = tx.Where("resource_type = ? AND resource_id = ?", constants.ExchangeAssetTypeIotCard, card.ID).Delete(&model.AssetWallet{}).Error; err != nil {
				return errors.Wrap(errors.CodeDatabaseError, err, "清理旧钱包失败")
			}

			shopTag := uint(0)
			if card.ShopID != nil {
				shopTag = *card.ShopID
			}
			if err = tx.Create(&model.AssetWallet{ResourceType: constants.ExchangeAssetTypeIotCard, ResourceID: card.ID, Balance: 0, FrozenBalance: 0, Currency: "CNY", Status: 1, Version: 0, ShopIDTag: shopTag}).Error; err != nil {
				return errors.Wrap(errors.CodeDatabaseError, err, "创建新钱包失败")
			}
			return nil
		}

		var device model.Device
		if err = tx.Where("id = ?", order.OldAssetID).First(&device).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				return errors.New(errors.CodeAssetNotFound)
			}
			return errors.Wrap(errors.CodeDatabaseError, err, "查询旧设备失败")
		}
		if device.AssetStatus != 3 {
			return errors.New(errors.CodeExchangeAssetNotExchanged)
		}

		if err = tx.Model(&model.Device{}).Where("id = ?", device.ID).Updates(map[string]any{
			"generation":                         device.Generation + 1,
			"asset_status":                       1,
			"accumulated_recharge":               0,
			"first_commission_paid":              false,
			"accumulated_recharge_by_series":     "{}",
			"first_recharge_triggered_by_series": "{}",
			"updater":                            middleware.GetUserIDFromContext(ctx),
			"updated_at":                         time.Now(),
		}).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "重置旧设备转新状态失败")
		}

		if err = tx.Where("virtual_no = ?", device.VirtualNo).Delete(&model.PersonalCustomerDevice{}).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "清理个人客户绑定失败")
		}

		if err = tx.Where("resource_type = ? AND resource_id = ?", constants.ExchangeAssetTypeDevice, device.ID).Delete(&model.AssetWallet{}).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "清理旧钱包失败")
		}

		shopTag := uint(0)
		if device.ShopID != nil {
			shopTag = *device.ShopID
		}
		if err = tx.Create(&model.AssetWallet{ResourceType: constants.ExchangeAssetTypeDevice, ResourceID: device.ID, Balance: 0, FrozenBalance: 0, Currency: "CNY", Status: 1, Version: 0, ShopIDTag: shopTag}).Error; err != nil {
			return errors.Wrap(errors.CodeDatabaseError, err, "创建新钱包失败")
		}
		return nil
	})
}

func (s *Service) GetPending(ctx context.Context, identifier string) (*dto.ClientExchangePendingResponse, error) {
	asset, err := s.resolveAssetByIdentifier(ctx, "", identifier)
	if err != nil {
		return nil, err
	}

	order, err := s.exchangeStore.FindActiveByOldAsset(ctx, asset.AssetType, asset.AssetID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询待处理换货单失败")
	}

	return &dto.ClientExchangePendingResponse{
		ID:             order.ID,
		ExchangeNo:     order.ExchangeNo,
		Status:         order.Status,
		StatusText:     exchangeStatusText(order.Status),
		ExchangeReason: order.ExchangeReason,
		CreatedAt:      order.CreatedAt,
	}, nil
}

func (s *Service) SubmitShippingInfo(ctx context.Context, id uint, req *dto.ClientShippingInfoRequest) error {
	updates := map[string]any{
		"recipient_name":    req.RecipientName,
		"recipient_phone":   req.RecipientPhone,
		"recipient_address": req.RecipientAddress,
		"updated_at":        time.Now(),
	}
	if err := s.exchangeStore.UpdateStatus(ctx, id, constants.ExchangeStatusPendingInfo, constants.ExchangeStatusPendingShip, updates); err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeExchangeStatusInvalid)
		}
		return errors.Wrap(errors.CodeDatabaseError, err, "提交收货信息失败")
	}
	return nil
}

type resolvedExchangeAsset struct {
	AssetType   string
	AssetID     uint
	Identifier  string
	VirtualNo   string
	AssetStatus int
	ShopID      *uint
	Card        *model.IotCard
	Device      *model.Device
}

func (s *Service) resolveAssetByIdentifier(ctx context.Context, expectedAssetType, identifier string) (*resolvedExchangeAsset, error) {
	if expectedAssetType == "" || expectedAssetType == constants.ExchangeAssetTypeDevice {
		device, err := s.deviceStore.GetByIdentifier(ctx, identifier)
		if err == nil {
			if expectedAssetType != "" && expectedAssetType != constants.ExchangeAssetTypeDevice {
				return nil, errors.New(errors.CodeExchangeAssetTypeMismatch)
			}
			return &resolvedExchangeAsset{AssetType: constants.ExchangeAssetTypeDevice, AssetID: device.ID, Identifier: identifier, VirtualNo: device.VirtualNo, AssetStatus: device.AssetStatus, ShopID: device.ShopID, Device: device}, nil
		}
		if err != gorm.ErrRecordNotFound {
			return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询设备失败")
		}
	}

	if expectedAssetType == "" || expectedAssetType == constants.ExchangeAssetTypeIotCard {
		var card model.IotCard
		query := s.db.WithContext(ctx).Where("virtual_no = ? OR iccid = ? OR msisdn = ?", identifier, identifier, identifier)
		query = middleware.ApplyShopFilter(ctx, query)
		if err := query.First(&card).Error; err == nil {
			if expectedAssetType != "" && expectedAssetType != constants.ExchangeAssetTypeIotCard {
				return nil, errors.New(errors.CodeExchangeAssetTypeMismatch)
			}
			return &resolvedExchangeAsset{AssetType: constants.ExchangeAssetTypeIotCard, AssetID: card.ID, Identifier: identifier, VirtualNo: card.VirtualNo, AssetStatus: card.AssetStatus, ShopID: card.ShopID, Card: &card}, nil
		} else if err != gorm.ErrRecordNotFound {
			return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询IoT卡失败")
		}
	}

	return nil, errors.New(errors.CodeAssetNotFound)
}

func (s *Service) toExchangeOrderResponse(order *model.ExchangeOrder) *dto.ExchangeOrderResponse {
	if order == nil {
		return nil
	}
	var deletedAt *time.Time
	if order.DeletedAt.Valid {
		deletedAt = &order.DeletedAt.Time
	}
	return &dto.ExchangeOrderResponse{
		ID:                 order.ID,
		ExchangeNo:         order.ExchangeNo,
		OldAssetType:       order.OldAssetType,
		OldAssetID:         order.OldAssetID,
		OldAssetIdentifier: order.OldAssetIdentifier,
		NewAssetType:       order.NewAssetType,
		NewAssetID:         order.NewAssetID,
		NewAssetIdentifier: order.NewAssetIdentifier,
		RecipientName:      order.RecipientName,
		RecipientPhone:     order.RecipientPhone,
		RecipientAddress:   order.RecipientAddress,
		ExpressCompany:     order.ExpressCompany,
		ExpressNo:          order.ExpressNo,
		MigrateData:        order.MigrateData,
		MigrationCompleted: order.MigrationCompleted,
		MigrationBalance:   order.MigrationBalance,
		ExchangeReason:     order.ExchangeReason,
		Remark:             order.Remark,
		Status:             order.Status,
		StatusText:         exchangeStatusText(order.Status),
		ShopID:             order.ShopID,
		CreatedAt:          order.CreatedAt,
		UpdatedAt:          order.UpdatedAt,
		DeletedAt:          deletedAt,
		Creator:            order.Creator,
		Updater:            order.Updater,
	}
}

func exchangeStatusText(status int) string {
	switch status {
	case constants.ExchangeStatusPendingInfo:
		return "待填写信息"
	case constants.ExchangeStatusPendingShip:
		return "待发货"
	case constants.ExchangeStatusShipped:
		return "已发货待确认"
	case constants.ExchangeStatusCompleted:
		return "已完成"
	case constants.ExchangeStatusCancelled:
		return "已取消"
	default:
		return "未知状态"
	}
}
