package bot import ( "context" "fmt" "strings" "time" "github.com/go-telegram/bot/models" "worktimeBot/internal/db" ) // handleSummaryMsg parses /summary [YYYY-MM] and sends a monthly summary. func (h *Handler) handleSummaryMsg(ctx context.Context, msg *models.Message) { user, err := h.getOrCreateUser(msg.Chat.ID) if err != nil { h.sendText(ctx, msg.Chat.ID, "Error loading profile") return } loc := loadLocation(user.Timezone) now := time.Now().In(loc) args := strings.Fields(msg.Text) if len(args) > 0 && args[0][0] == '/' { args = args[1:] } var calY, calM int if len(args) >= 1 { if _, err := fmt.Sscanf(args[0], "%d-%d", &calY, &calM); err != nil || calM < 1 || calM > 12 { h.sendText(ctx, msg.Chat.ID, "Usage: /summary [YYYY-MM] (e.g. /summary 2026-06)") return } } else { calY, calM = now.Year(), int(now.Month()) switch user.Calendar { case "jalali": calY, calM, _ = gregorianToJalali(calY, calM, now.Day()) case "hijri": calY, calM, _ = gregorianToHijri(calY, calM, now.Day()) } } startStr, endStr := monthGregorianRange(user.Calendar, calY, calM) summary := h.buildMonthlySummary(user, calY, calM, startStr, endStr, loc) h.sendText(ctx, msg.Chat.ID, summary) } // buildMonthlySummary returns a formatted summary for a calendar month. func (h *Handler) buildMonthlySummary(user *db.User, calY, calM int, startStr, endStr string, loc *time.Location) string { days, err := h.DB.GetUserDaysInRange(user.ID, startStr, endStr) if err != nil { return "Error loading days" } title := formatMonthTitle(user.Calendar, calY, calM) totalWork := int64(0) totalBreak := int64(0) dayOffCount := 0 workDayCount := 0 dayDetails := []string{} for _, day := range days { dayLabel := formatDateForCalendar(day.Date, user.Calendar) if day.IsDayOff { dayOffCount++ } events, err := h.DB.EventsForDayByDayID(day.ID) if err != nil || len(events) == 0 { if day.IsDayOff { dayDetails = append(dayDetails, fmt.Sprintf(" %s - Day Off", dayLabel)) } continue } y, m, d := parseGregorianDateKey(day.Date) dayStart := time.Date(y, time.Month(m), d, 0, 0, 0, 0, loc).Unix() dayEnd := time.Date(y, time.Month(m), d, 23, 59, 59, 0, loc).Unix() totals := ComputeDailyTotals(events, day.MinBreakThreshold, dayStart, dayEnd) if totals.TotalSeconds == 0 { if day.IsDayOff { dayDetails = append(dayDetails, fmt.Sprintf(" %s - Day Off", dayLabel)) } continue } workDayCount++ totalWork += totals.TotalSeconds totalBreak += totals.BreakSeconds label := fmt.Sprintf(" %s - %s worked", dayLabel, formatDuration(totals.TotalSeconds)) if day.IsDayOff { label += " (Day Off)" } dayDetails = append(dayDetails, label) } var b strings.Builder b.WriteString(fmt.Sprintf("Summary for %s\n", title)) b.WriteString(fmt.Sprintf("Work days: %d\n", workDayCount)) if dayOffCount > 0 { b.WriteString(fmt.Sprintf("Days off: %d\n", dayOffCount)) } b.WriteString(fmt.Sprintf("Total: %s worked", formatDuration(totalWork))) if totalBreak > 0 { b.WriteString(fmt.Sprintf(", %s break", formatDuration(totalBreak))) } st, _ := h.DB.GetOrCreateSettings(user.ID) if st.HourlyRate > 0 { est := computeMonthlySalary(days, h.DB, user.ID, st.HourlyRate, loc) b.WriteString(fmt.Sprintf("\nSalary: %s (%.1f hours, %d days)", est.formatSalary(st.Currency), est.WorkHours, est.WorkDays)) } b.WriteString("\n\nDetails:\n") for _, d := range dayDetails { b.WriteString(d) b.WriteString("\n") } return b.String() }