package exporter

import (
	"context"
	"strconv"
	"time"

	"gorm.io/gorm"

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

// PackageDataSource 套餐导出数据源。
type PackageDataSource struct {
	db *gorm.DB
}

// NewPackageDataSource 创建套餐导出数据源。
func NewPackageDataSource(db *gorm.DB) *PackageDataSource {
	return &PackageDataSource{db: db}
}

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

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

// Headers 返回套餐导出固定表头。
func (s *PackageDataSource) Headers(context.Context, ExportParams) ([]string, error) {
	return []string{
		"套餐编码", "套餐名称", "套餐系列名称", "套餐类型", "套餐时长(月)",
		"套餐时长说明", "套餐周期类型", "套餐天数", "真流量额度(MB)", "虚流量额度(MB)",
		"是否启用虚流量", "虚流量比例", "流量重置周期", "到期时间基准", "成本价(元)",
		"建议售价(元)", "价格配置状态", "状态", "上架状态", "是否赠送套餐",
		"创建人ID", "更新人ID", "创建时间", "更新时间", "删除时间",
	}, nil
}

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

	var items []packageExportRow
	query := s.applyFilters(s.baseQuery(ctx, params), params).
		Joins("LEFT JOIN tb_package_series AS ps ON ps.id = p.series_id AND ps.deleted_at IS NULL").
		Select(s.selectColumns(params)).
		Order("p.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, buildPackageExportRow(item))
	}
	return rows, nil
}

func (s *PackageDataSource) baseQuery(ctx context.Context, params ExportParams) *gorm.DB {
	query := s.db.WithContext(ctx).Table("tb_package AS p").Where("p.deleted_at IS NULL")
	switch params.UserType {
	case constants.UserTypeSuperAdmin, constants.UserTypePlatform:
		return query
	case constants.UserTypeAgent:
		if params.CreatorShopID == nil || *params.CreatorShopID == 0 {
			return query.Where("1 = 0")
		}
		return query.
			Joins(`INNER JOIN tb_shop_package_allocation AS a
				ON a.package_id = p.id
				AND a.shop_id = ?
				AND a.status = ?
				AND a.deleted_at IS NULL`, *params.CreatorShopID, constants.StatusEnabled).
			Where("p.is_gift = ?", false)
	default:
		return query.Where("1 = 0")
	}
}

func (s *PackageDataSource) applyFilters(query *gorm.DB, params ExportParams) *gorm.DB {
	if packageName, ok := filterString(params.Filters, "package_name"); ok {
		query = query.Where("p.package_name LIKE ?", "%"+packageName+"%")
	}
	if seriesID, ok := filterUint(params.Filters, "series_id"); ok {
		query = query.Where("p.series_id = ?", seriesID)
	}
	if status, ok := filterInt(params.Filters, "status"); ok {
		query = query.Where("p.status = ?", status)
	}
	if shelfStatus, ok := filterInt(params.Filters, "shelf_status"); ok {
		if params.UserType == constants.UserTypeAgent {
			query = query.Where("a.shelf_status = ?", shelfStatus)
		} else {
			query = query.Where("p.shelf_status = ?", shelfStatus)
		}
	}
	if packageType, ok := filterString(params.Filters, "package_type"); ok {
		query = query.Where("p.package_type = ?", packageType)
	}
	return query
}

func (s *PackageDataSource) selectColumns(params ExportParams) string {
	costPriceColumn := "p.cost_price"
	shelfStatusColumn := "p.shelf_status"
	if params.UserType == constants.UserTypeAgent {
		costPriceColumn = "a.cost_price"
		shelfStatusColumn = "a.shelf_status"
	}
	return `
		p.package_code,
		p.package_name,
		COALESCE(ps.series_name, '') AS series_name,
		p.package_type,
		p.duration_months,
		p.calendar_type,
		p.duration_days,
		p.real_data_mb,
		p.virtual_data_mb,
		p.enable_virtual_data,
		p.virtual_ratio,
		p.data_reset_cycle,
		p.expiry_base,
		` + costPriceColumn + ` AS cost_price,
		p.suggested_retail_price,
		p.price_config_status,
		p.status,
		` + shelfStatusColumn + ` AS shelf_status,
		p.is_gift,
		p.creator,
		p.updater,
		p.created_at,
		p.updated_at`
}

type packageExportRow struct {
	PackageCode          string    `gorm:"column:package_code"`
	PackageName          string    `gorm:"column:package_name"`
	SeriesName           string    `gorm:"column:series_name"`
	PackageType          string    `gorm:"column:package_type"`
	DurationMonths       int       `gorm:"column:duration_months"`
	CalendarType         string    `gorm:"column:calendar_type"`
	DurationDays         int       `gorm:"column:duration_days"`
	RealDataMB           int64     `gorm:"column:real_data_mb"`
	VirtualDataMB        int64     `gorm:"column:virtual_data_mb"`
	EnableVirtualData    bool      `gorm:"column:enable_virtual_data"`
	VirtualRatio         float64   `gorm:"column:virtual_ratio"`
	DataResetCycle       string    `gorm:"column:data_reset_cycle"`
	ExpiryBase           string    `gorm:"column:expiry_base"`
	CostPrice            int64     `gorm:"column:cost_price"`
	SuggestedRetailPrice int64     `gorm:"column:suggested_retail_price"`
	PriceConfigStatus    int       `gorm:"column:price_config_status"`
	Status               int       `gorm:"column:status"`
	ShelfStatus          int       `gorm:"column:shelf_status"`
	IsGift               bool      `gorm:"column:is_gift"`
	Creator              uint      `gorm:"column:creator"`
	Updater              uint      `gorm:"column:updater"`
	CreatedAt            time.Time `gorm:"column:created_at"`
	UpdatedAt            time.Time `gorm:"column:updated_at"`
}

func buildPackageExportRow(item packageExportRow) []string {
	return []string{
		item.PackageCode,
		item.PackageName,
		item.SeriesName,
		constants.GetPackageTypeName(item.PackageType),
		strconv.Itoa(item.DurationMonths),
		formatPackageDurationDescription(item),
		constants.GetPackageCalendarTypeName(item.CalendarType),
		strconv.Itoa(item.DurationDays),
		strconv.FormatInt(item.RealDataMB, 10),
		strconv.FormatInt(item.VirtualDataMB, 10),
		formatYesNo(item.EnableVirtualData),
		strconv.FormatFloat(item.VirtualRatio, 'f', 6, 64),
		constants.GetPackageDataResetCycleName(item.DataResetCycle),
		constants.GetPackageExpiryBaseName(item.ExpiryBase),
		formatMoneyYuan(item.CostPrice),
		formatPackageSuggestedRetailPrice(item),
		constants.GetPackagePriceConfigStatusName(item.PriceConfigStatus),
		constants.GetStatusName(item.Status),
		constants.GetShelfStatusName(item.ShelfStatus),
		formatYesNo(item.IsGift),
		strconv.FormatUint(uint64(item.Creator), 10),
		strconv.FormatUint(uint64(item.Updater), 10),
		item.CreatedAt.Format(exportTimeLayout),
		item.UpdatedAt.Format(exportTimeLayout),
		"",
	}
}

func formatPackageDurationDescription(item packageExportRow) string {
	if item.CalendarType == constants.PackageCalendarTypeByDay && item.DurationDays > 0 {
		return strconv.Itoa(item.DurationDays) + "天"
	}
	if item.DurationMonths > 0 {
		return strconv.Itoa(item.DurationMonths) + "个月"
	}
	return ""
}

func formatPackageSuggestedRetailPrice(item packageExportRow) string {
	if item.PriceConfigStatus == constants.PackagePriceConfigStatusUnconfigured {
		return ""
	}
	return formatMoneyYuan(item.SuggestedRetailPrice)
}

func formatYesNo(value bool) string {
	if value {
		return "是"
	}
	return "否"
}
