package health

import (
	"context"
	"fmt"
	"time"

	"apps/backend/internal/logic/growthmap"
	"apps/backend/internal/middleware"
	growthUC "apps/backend/internal/module/growth/usecase"
	studioDomain "apps/backend/internal/module/studio/domain"
	"apps/backend/internal/svc"
	"apps/backend/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type ListAccountHealthLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewListAccountHealthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListAccountHealthLogic {
	return &ListAccountHealthLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
}

func (l *ListAccountHealthLogic) ListAccountHealth() (*types.AccountHealthListData, error) {
	uid, ok := middleware.UIDFrom(l.ctx)
	if !ok || uid <= 0 {
		return nil, fmt.Errorf("unauthorized")
	}
	accs, err := l.svcCtx.Threads.List(l.ctx, uid)
	if err != nil {
		return nil, err
	}
	// gather outbox stats per account
	type stats struct {
		sends24h, fails7d, total7d, intervalViol int
	}
	byAcc := map[string]*stats{}
	now := time.Now().UTC()
	dayAgo := now.Add(-24 * time.Hour).UnixNano()
	weekAgo := now.Add(-7 * 24 * time.Hour).UnixNano()
	if l.svcCtx.Studio != nil {
		if boxes, oerr := l.svcCtx.Studio.ListOutbox(l.ctx, uid); oerr == nil {
			for _, b := range boxes {
				if b == nil {
					continue
				}
				var prevAt int64
				for _, st := range b.Steps {
					s := byAcc[st.AccountID]
					if s == nil {
						s = &stats{}
						byAcc[st.AccountID] = s
					}
					ts := st.PublishedAt
					if ts == 0 {
						ts = st.ScheduledAt
					}
					if st.Status == studioDomain.StepPublished || st.Status == studioDomain.StepFailed || st.Status == studioDomain.StepPublishing {
						if ts >= dayAgo {
							s.sends24h++
						}
						if ts >= weekAgo {
							s.total7d++
							if st.Status == studioDomain.StepFailed {
								s.fails7d++
							}
						}
					}
					if prevAt > 0 && st.ScheduledAt > 0 {
						gap := st.ScheduledAt - prevAt
						// less than 2 minutes between steps → interval violation
						if gap > 0 && gap < int64(2*time.Minute) {
							s.intervalViol++
						}
					}
					if st.ScheduledAt > 0 {
						prevAt = st.ScheduledAt
					}
				}
			}
		}
	}
	var list []*types.AccountHealthPublic
	for _, a := range accs {
		if a == nil {
			continue
		}
		sig := growthUC.HealthSignals{}
		if s := byAcc[a.ID]; s != nil {
			sig.Sends24h = s.sends24h
			sig.IntervalViolations = s.intervalViol
			if s.total7d > 0 {
				sig.FailRate7d = float64(s.fails7d) / float64(s.total7d)
			}
		}
		sig.TokenBad = !a.IsUsable || a.Connection == "error" || a.Connection == "disconnected"
		h, herr := l.svcCtx.Growth.RecomputeHealth(l.ctx, uid, a.ID, sig)
		if herr != nil {
			continue
		}
		list = append(list, growthmap.Health(h))
	}
	// flatten for response type
	out := make([]types.AccountHealthPublic, 0, len(list))
	for _, h := range list {
		if h != nil {
			out = append(out, *h)
		}
	}
	return &types.AccountHealthListData{List: out}, nil
}
