// Package bot implements the Telegram bot logic: message routing, time tracking, // calendar rendering, Excel export, and inline editing of events. package bot import ( "sort" "worktimeBot/internal/db" ) // State represents the current tracking state during daily totals computation. type State int const ( // StateIdle indicates no active work session. StateIdle State = iota // StateWorking indicates an active work session. StateWorking // StateOnBreak indicates an active break session. StateOnBreak ) // DailyTotals holds the computed total work and break seconds for a single day. type DailyTotals struct { TotalSeconds int64 BreakSeconds int64 } // ComputeDailyTotals calculates the total work and break time from a list of events. // It sorts events chronologically, inserts synthetic start/end events when needed, // and tracks state transitions between idle, working, and on-break. func ComputeDailyTotals(events []db.Event, minBreakThreshold int64, dayStart, dayEnd int64) DailyTotals { if len(events) == 0 { return DailyTotals{} } sorted := make([]db.Event, len(events)) copy(sorted, events) sort.Slice(sorted, func(i, j int) bool { return sorted[i].OccurredAt < sorted[j].OccurredAt }) if dayStart > 0 && len(sorted) > 0 && sorted[0].EventType == "out" { first := db.Event{EventType: "in", OccurredAt: dayStart} sorted = append([]db.Event{first}, sorted...) } if dayEnd > 0 && len(sorted) > 0 && sorted[len(sorted)-1].EventType == "in" { last := db.Event{EventType: "out", OccurredAt: dayEnd} sorted = append(sorted, last) } var workTotal, breakTotal int64 var workStart, breakStart int64 state := StateIdle for _, e := range sorted { switch e.EventType { case "in": switch state { case StateIdle: workStart = e.OccurredAt state = StateWorking case StateOnBreak: breakDuration := e.OccurredAt - breakStart if minBreakThreshold > 0 && breakDuration <= minBreakThreshold { workStart = breakStart } else { breakTotal += breakDuration workStart = e.OccurredAt } state = StateWorking } case "out": switch state { case StateWorking: workTotal += e.OccurredAt - workStart workStart = 0 breakStart = e.OccurredAt state = StateOnBreak case StateIdle: breakStart = e.OccurredAt state = StateOnBreak case StateOnBreak: breakStart = e.OccurredAt } } } return DailyTotals{ TotalSeconds: workTotal, BreakSeconds: breakTotal, } }