package services

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"quyun/v2/app/errorx"
	creator_dto "quyun/v2/app/http/v1/dto"
	"quyun/v2/database/models"
	"quyun/v2/pkg/consts"
)

type reportRange struct {
	startDay time.Time
	endDay   time.Time
	endNext  time.Time
}

func (s *creator) ReportOverview(
	ctx context.Context,
	tenantID int64,
	userID int64,
	filter *creator_dto.ReportOverviewFilter,
) (*creator_dto.ReportOverviewResponse, error) {
	// 校验租户归属（仅租户主可查看统计）。
	tid, err := s.getTenantID(ctx, tenantID, userID)
	if err != nil {
		return nil, err
	}

	// 统一统计时间范围与粒度。
	rg, err := s.normalizeReportRange(filter)
	if err != nil {
		return nil, err
	}

	// 统计累计曝光（全量累计值，暂无按时间拆分的曝光记录）。
	var totalViews int64
	err = models.ContentQuery.WithContext(ctx).
		UnderlyingDB().
		Model(&models.Content{}).
		Select("coalesce(sum(views), 0)").
		Where("tenant_id = ?", tid).
		Scan(&totalViews).Error
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	// 订单仅统计内容购买类型，并按状态划分已支付/已退款。
	paidCount, paidAmount, err := s.orderAggregate(ctx, tid, consts.OrderStatusPaid, "paid_at", rg)
	if err != nil {
		return nil, err
	}
	refundCount, refundAmount, err := s.orderAggregate(ctx, tid, consts.OrderStatusRefunded, "updated_at", rg)
	if err != nil {
		return nil, err
	}

	conversionRate := 0.0
	if totalViews > 0 {
		conversionRate = float64(paidCount) / float64(totalViews)
	}

	// 生成按日趋势序列。
	paidSeries, err := s.orderSeries(ctx, tid, consts.OrderStatusPaid, "paid_at", rg)
	if err != nil {
		return nil, err
	}
	refundSeries, err := s.orderSeries(ctx, tid, consts.OrderStatusRefunded, "updated_at", rg)
	if err != nil {
		return nil, err
	}

	items := make([]creator_dto.ReportOverviewItem, 0)
	for day := rg.startDay; !day.After(rg.endDay); day = day.AddDate(0, 0, 1) {
		key := day.Format("2006-01-02")
		paidItem := paidSeries[key]
		refundItem := refundSeries[key]
		items = append(items, creator_dto.ReportOverviewItem{
			Date:         key,
			PaidOrders:   paidItem.Count,
			PaidAmount:   float64(paidItem.Amount) / 100.0,
			RefundOrders: refundItem.Count,
			RefundAmount: float64(refundItem.Amount) / 100.0,
		})
	}

	return &creator_dto.ReportOverviewResponse{
		Summary: creator_dto.ReportSummary{
			TotalViews:     totalViews,
			PaidOrders:     paidCount,
			PaidAmount:     float64(paidAmount) / 100.0,
			RefundOrders:   refundCount,
			RefundAmount:   float64(refundAmount) / 100.0,
			ConversionRate: conversionRate,
		},
		Items: items,
	}, nil
}

func (s *creator) ExportReport(
	ctx context.Context,
	tenantID int64,
	userID int64,
	form *creator_dto.ReportExportForm,
) (*creator_dto.ReportExportResponse, error) {
	if form == nil {
		return nil, errorx.ErrBadRequest.WithMsg("导出参数不能为空")
	}
	format := strings.ToLower(strings.TrimSpace(form.Format))
	if format == "" {
		format = "csv"
	}
	if format != "csv" {
		return nil, errorx.ErrBadRequest.WithMsg("仅支持 CSV 导出")
	}

	// 导出逻辑复用概览统计，保证口径一致。
	overview, err := s.ReportOverview(ctx, tenantID, userID, &creator_dto.ReportOverviewFilter{
		StartAt:     form.StartAt,
		EndAt:       form.EndAt,
		Granularity: form.Granularity,
	})
	if err != nil {
		return nil, err
	}

	builder := &strings.Builder{}
	builder.WriteString("date,paid_orders,paid_amount,refund_orders,refund_amount\n")
	for _, item := range overview.Items {
		builder.WriteString(item.Date)
		builder.WriteString(",")
		builder.WriteString(strconv.FormatInt(item.PaidOrders, 10))
		builder.WriteString(",")
		builder.WriteString(formatAmount(item.PaidAmount))
		builder.WriteString(",")
		builder.WriteString(strconv.FormatInt(item.RefundOrders, 10))
		builder.WriteString(",")
		builder.WriteString(formatAmount(item.RefundAmount))
		builder.WriteString("\n")
	}

	filename := fmt.Sprintf("report_overview_%s.csv", time.Now().Format("20060102_150405"))
	return &creator_dto.ReportExportResponse{
		Filename: filename,
		MimeType: "text/csv",
		Content:  builder.String(),
	}, nil
}

type reportAggRow struct {
	Day    time.Time `gorm:"column:day"`
	Count  int64     `gorm:"column:count"`
	Amount int64     `gorm:"column:amount"`
}

func (s *creator) orderAggregate(
	ctx context.Context,
	tenantID int64,
	status consts.OrderStatus,
	timeField string,
	rg reportRange,
) (int64, int64, error) {
	var total struct {
		Count  int64 `gorm:"column:count"`
		Amount int64 `gorm:"column:amount"`
	}
	err := models.OrderQuery.WithContext(ctx).
		UnderlyingDB().
		Model(&models.Order{}).
		Select("count(*) as count, coalesce(sum(amount_paid), 0) as amount").
		Where("tenant_id = ? AND type = ? AND status = ? AND "+timeField+" >= ? AND "+timeField+" < ?",
			tenantID, consts.OrderTypeContentPurchase, status, rg.startDay, rg.endNext).
		Scan(&total).Error
	if err != nil {
		return 0, 0, errorx.ErrDatabaseError.WithCause(err)
	}
	return total.Count, total.Amount, nil
}

func (s *creator) orderSeries(
	ctx context.Context,
	tenantID int64,
	status consts.OrderStatus,
	timeField string,
	rg reportRange,
) (map[string]reportAggRow, error) {
	rows := make([]reportAggRow, 0)
	err := models.OrderQuery.WithContext(ctx).
		UnderlyingDB().
		Model(&models.Order{}).
		Select("date_trunc('day', "+timeField+") as day, count(*) as count, coalesce(sum(amount_paid), 0) as amount").
		Where("tenant_id = ? AND type = ? AND status = ? AND "+timeField+" >= ? AND "+timeField+" < ?",
			tenantID, consts.OrderTypeContentPurchase, status, rg.startDay, rg.endNext).
		Group("day").
		Scan(&rows).Error
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	result := make(map[string]reportAggRow, len(rows))
	for _, row := range rows {
		key := row.Day.Format("2006-01-02")
		result[key] = row
	}
	return result, nil
}

func (s *creator) normalizeReportRange(filter *creator_dto.ReportOverviewFilter) (reportRange, error) {
	granularity := "day"
	if filter != nil && filter.Granularity != nil && strings.TrimSpace(*filter.Granularity) != "" {
		granularity = strings.ToLower(strings.TrimSpace(*filter.Granularity))
	}
	if granularity != "day" {
		return reportRange{}, errorx.ErrBadRequest.WithMsg("仅支持按天统计")
	}

	now := time.Now()
	endAt := now
	if filter != nil && filter.EndAt != nil && strings.TrimSpace(*filter.EndAt) != "" {
		parsed, err := time.Parse(time.RFC3339, strings.TrimSpace(*filter.EndAt))
		if err != nil {
			return reportRange{}, errorx.ErrBadRequest.WithMsg("结束时间格式错误")
		}
		endAt = parsed
	}

	startAt := endAt.AddDate(0, 0, -6)
	if filter != nil && filter.StartAt != nil && strings.TrimSpace(*filter.StartAt) != "" {
		parsed, err := time.Parse(time.RFC3339, strings.TrimSpace(*filter.StartAt))
		if err != nil {
			return reportRange{}, errorx.ErrBadRequest.WithMsg("开始时间格式错误")
		}
		startAt = parsed
	}

	startDay := time.Date(startAt.Year(), startAt.Month(), startAt.Day(), 0, 0, 0, 0, startAt.Location())
	endDay := time.Date(endAt.Year(), endAt.Month(), endAt.Day(), 0, 0, 0, 0, endAt.Location())
	if endDay.Before(startDay) {
		return reportRange{}, errorx.ErrBadRequest.WithMsg("结束时间不能早于开始时间")
	}

	endNext := endDay.AddDate(0, 0, 1)
	return reportRange{
		startDay: startDay,
		endDay:   endDay,
		endNext:  endNext,
	}, nil
}

func formatAmount(amount float64) string {
	return strconv.FormatFloat(amount, 'f', 2, 64)
}
