package channel

import (
	"context"
	"time"

	"bindbox-game/internal/pkg/logger"
	"bindbox-game/internal/repository/mysql"
	"bindbox-game/internal/repository/mysql/dao"
	"bindbox-game/internal/repository/mysql/model"
)

type Service interface {
	Create(ctx context.Context, in CreateInput) (*model.Channels, error)
	Modify(ctx context.Context, id int64, in ModifyInput) error
	Delete(ctx context.Context, id int64) error
	List(ctx context.Context, in ListInput) (items []*ChannelWithStat, total int64, err error)
	GetStats(ctx context.Context, channelID int64, days int, startDate, endDate string) (*StatsOutput, error)
}

type service struct {
	logger  logger.CustomLogger
	readDB  *dao.Query
	writeDB *dao.Query
}

func New(l logger.CustomLogger, db mysql.Repo) Service {
	return &service{logger: l, readDB: dao.Use(db.GetDbR()), writeDB: dao.Use(db.GetDbW())}
}

type CreateInput struct {
	Name    string
	Code    string
	Type    string
	Remarks string
}

type ModifyInput struct {
	Name    *string
	Type    *string
	Remarks *string
}

type ListInput struct {
	Name     string
	Page     int
	PageSize int
}

type ChannelWithStat struct {
	*model.Channels
	UserCount int64 `json:"user_count"`
}

type StatsOutput struct {
	Overview StatsOverview    `json:"overview"`
	Daily    []StatsDailyItem `json:"daily"`
}

type StatsOverview struct {
	TotalUsers  int64 `json:"total_users"`
	TotalOrders int64 `json:"total_orders"`
	TotalGMV    int64 `json:"total_gmv"`
}

type StatsDailyItem struct {
	Date       string `json:"date"`
	UserCount  int64  `json:"user_count"`
	OrderCount int64  `json:"order_count"`
	GMV        int64  `json:"gmv"`
}

func (s *service) Create(ctx context.Context, in CreateInput) (*model.Channels, error) {
	m := &model.Channels{Name: in.Name, Code: in.Code, Type: in.Type, Remarks: in.Remarks}
	if err := s.writeDB.Channels.WithContext(ctx).Create(m); err != nil {
		return nil, err
	}
	return m, nil
}

func (s *service) Modify(ctx context.Context, id int64, in ModifyInput) error {
	updater := s.writeDB.Channels.WithContext(ctx).Where(s.writeDB.Channels.ID.Eq(id))
	set := map[string]any{}
	if in.Name != nil {
		set["name"] = *in.Name
	}
	if in.Type != nil {
		set["type"] = *in.Type
	}
	if in.Remarks != nil {
		set["remarks"] = *in.Remarks
	}
	if len(set) == 0 {
		return nil
	}
	_, err := updater.Updates(set)
	return err
}

func (s *service) Delete(ctx context.Context, id int64) error {
	_, err := s.writeDB.Channels.WithContext(ctx).Where(s.writeDB.Channels.ID.Eq(id)).Delete()
	return err
}

func (s *service) List(ctx context.Context, in ListInput) (items []*ChannelWithStat, total int64, err error) {
	if in.Page <= 0 {
		in.Page = 1
	}
	if in.PageSize <= 0 {
		in.PageSize = 20
	}
	q := s.readDB.Channels.WithContext(ctx)
	if in.Name != "" {
		q = q.Where(s.readDB.Channels.Name.Like("%" + in.Name + "%"))
	}

	total, err = q.Count()
	if err != nil {
		return
	}

	// List channels
	channels, err := q.Order(s.readDB.Channels.ID.Desc()).Limit(in.PageSize).Offset((in.Page - 1) * in.PageSize).Find()
	if err != nil {
		return
	}

	// Get user counts
	var channelIDs []int64
	for _, c := range channels {
		channelIDs = append(channelIDs, c.ID)
	}

	stats := make(map[int64]int64)
	if len(channelIDs) > 0 {
		type Result struct {
			ChannelID int64
			Count     int64
		}
		var results []Result
		// Using raw query for grouping
		err = s.readDB.Users.WithContext(ctx).UnderlyingDB().Table("users").
			Select("channel_id, count(*) as count").
			Where("channel_id IN ?", channelIDs).
			Group("channel_id").
			Scan(&results).Error
		if err == nil {
			for _, r := range results {
				stats[r.ChannelID] = r.Count
			}
		}
	}

	for _, c := range channels {
		items = append(items, &ChannelWithStat{
			Channels:  c,
			UserCount: stats[c.ID],
		})
	}
	return
}

func (s *service) GetStats(ctx context.Context, channelID int64, months int, startDateStr, endDateStr string) (*StatsOutput, error) {
	now := time.Now()
	var startDate, endDate time.Time

	// 如果指定了日期范围，使用自定义日期
	if startDateStr != "" && endDateStr != "" {
		var err error
		startDate, err = time.Parse("2006-01-02", startDateStr)
		if err != nil {
			return nil, err
		}
		endDate, err = time.Parse("2006-01-02", endDateStr)
		if err != nil {
			return nil, err
		}
		// 确保 endDate 是当天结束
		endDate = endDate.Add(24*time.Hour - time.Second)
	} else {
		// 默认按月份计算
		if months <= 0 {
			months = 12
		}
		startMonth := now.AddDate(0, -months+1, 0)
		startDate = time.Date(startMonth.Year(), startMonth.Month(), 1, 0, 0, 0, 0, startMonth.Location())
		endDate = now
	}

	out := &StatsOutput{}

	// 1. Overview
	// Users
	userCount, _ := s.readDB.Users.WithContext(ctx).Where(s.readDB.Users.ChannelID.Eq(channelID)).Count()
	out.Overview.TotalUsers = userCount

	// Orders & GMV
	type OrderStat struct {
		Count int64
		GMV   int64
	}
	var os OrderStat
	s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders").
		Joins("JOIN users ON users.id = orders.user_id").
		Select("count(*) as count, coalesce(sum(actual_amount),0) as gmv").
		Where("users.channel_id = ? AND orders.status = 2", channelID).
		Scan(&os)
	out.Overview.TotalOrders = os.Count
	out.Overview.TotalGMV = os.GMV

	// 2. Monthly Stats
	dateMap := make(map[string]*StatsDailyItem)
	var dateList []string
	for i := 0; i < months; i++ {
		d := startDate.AddDate(0, i, 0).Format("2006-01")
		dateList = append(dateList, d)
		dateMap[d] = &StatsDailyItem{Date: d}
	}

	// Monthly Users
	type MonthlyUser struct {
		Date  string
		Count int64
	}
	var monthlyUsers []MonthlyUser
	s.readDB.Users.WithContext(ctx).UnderlyingDB().Table("users").
		Select("DATE_FORMAT(created_at, '%Y-%m') as date, count(*) as count").
		Where("channel_id = ? AND created_at >= ?", channelID, startDate).
		Group("date").Scan(&monthlyUsers)

	for _, u := range monthlyUsers {
		if item, ok := dateMap[u.Date]; ok {
			item.UserCount = u.Count
		}
	}

	// Monthly Orders
	type MonthlyOrder struct {
		Date  string
		Count int64
		GMV   int64
	}
	var monthlyOrders []MonthlyOrder
	s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders").
		Joins("JOIN users ON users.id = orders.user_id").
		Select("DATE_FORMAT(orders.created_at, '%Y-%m') as date, count(*) as count, coalesce(sum(actual_amount),0) as gmv").
		Where("users.channel_id = ? AND orders.status = 2 AND orders.created_at >= ?", channelID, startDate).
		Group("date").Scan(&monthlyOrders)

	for _, o := range monthlyOrders {
		if item, ok := dateMap[o.Date]; ok {
			item.OrderCount = o.Count
			item.GMV = o.GMV
		}
	}

	for _, d := range dateList {
		out.Daily = append(out.Daily, *dateMap[d])
	}

	return out, nil
}
