package service

import "github.com/tidbcloud/aisupport/services/oncall/models"

// Reference layers let a layer's on-call come from another team's current
// primary instead of its own people, gated by the layer's window and combined
// via override/append — the same fold as people layers, so a team may freely mix
// people and reference layers. At read/freeze time a reference layer is resolved
// read-through (and frozen as concrete emails). Referenced teams are people-only
// (enforced on save), so resolution recurses at most one level and cannot cycle.

// refRow is one (email, interval) slice of a referenced team's primary schedule.
type refRow struct {
	start, end int64
	email      string
}

// refEmailsAt returns the emails on call in the timeline at instant at.
func refEmailsAt(rows []refRow, at int64) []string {
	out := make([]string, 0)
	for _, r := range rows {
		if r.start <= at && at < r.end {
			out = append(out, r.email)
		}
	}
	return out
}

// batchRefTimelines resolves the primary on-call timelines of every team
// referenced anywhere in layers over [from, to) in ONE batched nested call,
// keyed by source team id so the result can be shared across every team whose
// layers reference those sources. Returns nil when there are no reference layers.
//
// This is the fan-out-free counterpart to refTimelinesFor: the overview resolves
// all referenced teams once here instead of re-invoking GetEffectiveScheduleV2
// per reference layer (which also re-scanned the engineers table each time).
// Referenced teams are people-only (enforced on save), so the nested call has no
// reference layers of its own and does not recurse.
func (s *ScheduleService) batchRefTimelines(layers []models.TeamLayer, from, to int64) map[uint][]refRow {
	seen := make(map[uint]struct{})
	srcIDs := make([]uint, 0)
	for _, l := range layers {
		if l.SourceTeamID == 0 {
			continue
		}
		if _, ok := seen[l.SourceTeamID]; ok {
			continue
		}
		seen[l.SourceTeamID] = struct{}{}
		srcIDs = append(srcIDs, l.SourceTeamID)
	}
	if len(srcIDs) == 0 {
		return nil
	}
	rows, err := s.GetEffectiveScheduleV2(srcIDs, from, to, false, false)
	if err != nil {
		return nil
	}
	out := make(map[uint][]refRow, len(srcIDs))
	for _, r := range rows {
		tid, _ := r["team_id"].(uint)
		if tid == 0 {
			continue
		}
		em, _ := r["engineer_email"].(string)
		st, _ := r["start_time"].(int64)
		en, _ := r["end_time"].(int64)
		if em == "" || en <= st {
			continue
		}
		out[tid] = append(out[tid], refRow{start: st, end: en, email: em})
	}
	return out
}

// refTimelinesFor resolves the primary on-call timeline of every team referenced
// by the layer set over [from, to). Returns nil when there are no reference
// layers (the common people-only case), so callers pay nothing extra for it.
func (s *ScheduleService) refTimelinesFor(layers []models.TeamLayer, from, to int64) map[uint][]refRow {
	var out map[uint][]refRow
	for _, l := range layers {
		if l.SourceTeamID == 0 {
			continue
		}
		if _, ok := out[l.SourceTeamID]; ok {
			continue
		}
		if out == nil {
			out = make(map[uint][]refRow)
		}
		rows, err := s.GetEffectiveScheduleV2([]uint{l.SourceTeamID}, from, to, false, false)
		if err != nil {
			out[l.SourceTeamID] = nil
			continue
		}
		tl := make([]refRow, 0, len(rows))
		for _, r := range rows {
			st, _ := r["start_time"].(int64)
			en, _ := r["end_time"].(int64)
			em, _ := r["engineer_email"].(string)
			if em == "" || en <= st {
				continue
			}
			tl = append(tl, refRow{start: st, end: en, email: em})
		}
		out[l.SourceTeamID] = tl
	}
	return out
}
