package exporter

import (
	"context"
	"strconv"
	"time"

	"gorm.io/gorm"

	"github.com/break/junhong_cmp_fiber/pkg/constants"
)

// IotCardDataSource IoT 卡导出数据源。
type IotCardDataSource struct {
	db *gorm.DB
}

// NewIotCardDataSource 创建 IoT 卡导出数据源。
func NewIotCardDataSource(db *gorm.DB) *IotCardDataSource {
	return &IotCardDataSource{db: db}
}

// Scene 返回导出场景编码。
func (s *IotCardDataSource) Scene() string {
	return constants.ExportTaskSceneIotCard
}

// Count 统计 IoT 卡导出行数。
func (s *IotCardDataSource) Count(ctx context.Context, params ExportParams) (int, error) {
	var total int64
	query := s.applyFilters(ctx, s.baseQuery(ctx), params)
	if err := query.Count(&total).Error; err != nil {
		return 0, err
	}
	return int(total), nil
}

// Headers 返回 IoT 卡导出表头。
func (s *IotCardDataSource) Headers(ctx context.Context, params ExportParams) ([]string, error) {
	return []string{"ICCID", "MSISDN", "绑定设备虚拟号", "运营商", "店铺名称", "绑定设备名称", "是否实名", "实名时间", "网络状态", "套餐名称", "使用流量(MB)", "剩余流量(MB)"}, nil
}

// Fetch 按 offset/limit 查询 IoT 卡导出数据。
func (s *IotCardDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
	if limit <= 0 {
		return [][]string{}, nil
	}

	var items []iotCardExportRow
	query := s.applyFilters(ctx, s.baseQuery(ctx), params).
		Select(`
			c.iccid,
			c.msisdn,
			c.device_virtual_no,
			c.carrier_name,
			COALESCE(sh.shop_name, '') AS shop_name,
			COALESCE(d.device_name, '') AS device_name,
			c.real_name_status,
			c.first_realname_at,
			c.network_status,
			COALESCE(pkg.package_name, '') AS package_name,
			COALESCE(pkg.data_usage_mb, 0) AS data_usage_mb,
			COALESCE(pkg.data_limit_mb, 0) AS data_limit_mb
		`).
		Order("c.id ASC").
		Limit(limit).
		Offset(offset)
	if err := query.Scan(&items).Error; err != nil {
		return nil, err
	}

	rows := make([][]string, 0, len(items))
	for _, item := range items {
		rows = append(rows, []string{
			item.ICCID,
			item.MSISDN,
			item.DeviceVirtualNo,
			item.CarrierName,
			item.ShopName,
			item.DeviceName,
			formatRealNameVerified(item.RealNameStatus),
			formatOptionalTime(item.FirstRealnameAt),
			constants.GetNetworkStatusName(item.NetworkStatus),
			item.PackageName,
			strconv.FormatInt(item.DataUsageMB, 10),
			strconv.FormatInt(remainingPackageDataMB(item.DataLimitMB, item.DataUsageMB), 10),
		})
	}
	return rows, nil
}

// baseQuery 构造卡导出的基础查询。
// 绑定设备名称从当前有效绑定中取一条，避免多绑定历史记录放大导出行数。
func (s *IotCardDataSource) baseQuery(ctx context.Context) *gorm.DB {
	return s.db.WithContext(ctx).
		Table("tb_iot_card AS c").
		Joins("LEFT JOIN tb_shop AS sh ON sh.id = c.shop_id").
		Joins(`
			LEFT JOIN LATERAL (
				SELECT d.device_name
				FROM tb_device_sim_binding AS b
				JOIN tb_device AS d ON d.id = b.device_id AND d.deleted_at IS NULL
				WHERE b.iot_card_id = c.id
					AND b.bind_status = ?
					AND b.deleted_at IS NULL
				ORDER BY b.is_current DESC, b.id DESC
				LIMIT 1
			) AS d ON TRUE
		`, constants.BindStatusBound).
		Joins(`
			LEFT JOIN LATERAL (
				SELECT
					COALESCE(NULLIF(pu.package_name, ''), p.package_name, '') AS package_name,
					pu.data_usage_mb,
					pu.data_limit_mb
				FROM tb_package_usage AS pu
				LEFT JOIN tb_package AS p ON p.id = pu.package_id AND p.deleted_at IS NULL
				WHERE pu.iot_card_id = c.id
					AND pu.status = ?
					AND pu.master_usage_id IS NULL
					AND pu.deleted_at IS NULL
				ORDER BY pu.priority ASC, pu.activated_at ASC, pu.id ASC
				LIMIT 1
			) AS pkg ON TRUE
		`, constants.PackageUsageStatusActive).
		Where("c.deleted_at IS NULL")
}

// applyFilters 应用与 /api/admin/iot-cards/standalone 一致的筛选条件。
// 导出任务运行在 Worker 中，权限只能使用任务创建时保存的店铺范围快照。
func (s *IotCardDataSource) applyFilters(ctx context.Context, query *gorm.DB, params ExportParams) *gorm.DB {
	query = applyExportShopScope(query, params, "c.shop_id")

	if isStandalone, ok := filterOptionalBool(params.Filters, "is_standalone"); ok {
		query = query.Where("c.is_standalone = ?", isStandalone)
	}
	if status, ok := filterInt(params.Filters, "status"); ok && status > 0 {
		query = query.Where("c.status = ?", status)
	}
	if carrierID, ok := filterUint(params.Filters, "carrier_id"); ok {
		query = query.Where("c.carrier_id = ?", carrierID)
	}
	if shopIDs, ok := filterUintSlice(params.Filters, "shop_ids"); ok {
		if len(shopIDs) == 0 {
			query = query.Where("1 = 0")
		} else {
			query = query.Where("c.shop_id IN ?", shopIDs)
		}
	} else if shopID, ok := filterInt(params.Filters, "shop_id"); ok {
		if shopID <= 0 {
			query = query.Where("1 = 0")
		} else {
			query = query.Where("c.shop_id = ?", shopID)
		}
	}
	if iccid, ok := filterString(params.Filters, "iccid"); ok {
		query = query.Where("c.iccid LIKE ?", "%"+iccid+"%")
	}
	if msisdn, ok := filterString(params.Filters, "msisdn"); ok {
		query = query.Where("c.msisdn LIKE ?", "%"+msisdn+"%")
	}
	if virtualNo, ok := filterString(params.Filters, "virtual_no"); ok {
		query = query.Where("c.virtual_no LIKE ?", "%"+virtualNo+"%")
	}
	if keyword, ok := filterString(params.Filters, "keyword"); ok {
		query = query.Where("c.iccid LIKE ? OR c.virtual_no LIKE ?", "%"+keyword+"%", "%"+keyword+"%")
	}
	if batchNo, ok := filterString(params.Filters, "batch_no"); ok {
		query = query.Where("c.batch_no = ?", batchNo)
	}
	if packageID, ok := filterUint(params.Filters, "package_id"); ok {
		query = query.Where("c.id IN (?)",
			s.db.WithContext(ctx).Table("tb_package_usage").
				Select("iot_card_id").
				Where("package_id = ? AND deleted_at IS NULL", packageID))
	}
	if isDistributed, ok := filterOptionalBool(params.Filters, "is_distributed"); ok {
		if isDistributed {
			query = query.Where("c.shop_id IS NOT NULL")
		} else {
			query = query.Where("c.shop_id IS NULL")
		}
	}
	if iccidStart, ok := filterString(params.Filters, "iccid_start"); ok {
		query = query.Where("c.iccid >= ?", iccidStart)
	}
	if iccidEnd, ok := filterString(params.Filters, "iccid_end"); ok {
		query = query.Where("c.iccid <= ?", iccidEnd)
	}
	if isReplaced, ok := filterOptionalBool(params.Filters, "is_replaced"); ok {
		subQuery := s.db.WithContext(ctx).Table("tb_exchange_order").
			Select("old_asset_id").
			Where("old_asset_type = ? AND status IN ? AND deleted_at IS NULL",
				constants.ExchangeAssetTypeIotCard,
				[]int{constants.ExchangeStatusShipped, constants.ExchangeStatusCompleted},
			)
		if isReplaced {
			query = query.Where("c.id IN (?)", subQuery)
		} else {
			query = query.Where("c.id NOT IN (?)", subQuery)
		}
	}
	if seriesID, ok := filterUint(params.Filters, "series_id"); ok {
		query = query.Where("c.series_id = ?", seriesID)
	}
	if carrierName, ok := filterString(params.Filters, "carrier_name"); ok {
		query = query.Where("c.carrier_name LIKE ?", "%"+carrierName+"%")
	}
	if hasActivePackage, ok := filterOptionalBool(params.Filters, "has_active_package"); ok {
		subQuery := s.db.WithContext(ctx).Table("tb_package_usage AS pu").
			Select("1").
			Where("pu.iot_card_id = c.id AND pu.status = ? AND pu.master_usage_id IS NULL AND pu.deleted_at IS NULL", constants.PackageUsageStatusActive)
		if hasActivePackage {
			query = query.Where("EXISTS (?)", subQuery)
		} else {
			query = query.Where("NOT EXISTS (?)", subQuery)
		}
	}
	return query
}

type iotCardExportRow struct {
	ICCID           string     `gorm:"column:iccid"`
	MSISDN          string     `gorm:"column:msisdn"`
	DeviceVirtualNo string     `gorm:"column:device_virtual_no"`
	CarrierName     string     `gorm:"column:carrier_name"`
	ShopName        string     `gorm:"column:shop_name"`
	DeviceName      string     `gorm:"column:device_name"`
	RealNameStatus  int        `gorm:"column:real_name_status"`
	FirstRealnameAt *time.Time `gorm:"column:first_realname_at"`
	NetworkStatus   int        `gorm:"column:network_status"`
	PackageName     string     `gorm:"column:package_name"`
	DataUsageMB     int64      `gorm:"column:data_usage_mb"`
	DataLimitMB     int64      `gorm:"column:data_limit_mb"`
}

func remainingPackageDataMB(limit, used int64) int64 {
	remaining := limit - used
	if remaining < 0 {
		return 0
	}
	return remaining
}

func formatRealNameVerified(status int) string {
	switch status {
	case constants.RealNameStatusVerified:
		return "是"
	case constants.RealNameStatusNotVerified:
		return "否"
	default:
		return "未知"
	}
}
