package db

import (
	"fmt"
	"time"
)

type TimeRange struct {
	Start time.Time `json:"start"`
	End   time.Time `json:"end"`
}

func (tr *TimeRange) OverlapsExclusive(timeRange TimeRange) bool {
	start1 := tr.Start.UnixNano()
	end1 := tr.End.UnixNano()
	start2 := timeRange.Start.UnixNano()
	end2 := timeRange.End.UnixNano()
	return (start1 < end2) && (start2 < end1)
}

func (tr *TimeRange) OverlapsInclusive(timeRange TimeRange) bool {
	start1 := tr.Start.UnixNano()
	end1 := tr.End.UnixNano()
	start2 := timeRange.Start.UnixNano()
	end2 := timeRange.End.UnixNano()
	return (start1 <= end2) && (start2 <= end1)
}

func (tr *TimeRange) Merge(timeRange TimeRange) TimeRange {
	minStart := tr.Start
	if s := timeRange.Start; s.Before(minStart) {
		minStart = s
	}

	maxEnd := tr.End
	if e := timeRange.End; e.After(maxEnd) {
		maxEnd = e
	}

	return TimeRange{
		Start: minStart,
		End:   maxEnd,
	}
}

func (tr *TimeRange) MergeIfOverlaps(timeRange TimeRange) []TimeRange {
	if tr.OverlapsInclusive(timeRange) {
		merged := tr.Merge(timeRange)
		return []TimeRange{
			merged,
		}
	}

	return []TimeRange{
		*tr,
		timeRange,
	}
}

func (tr *TimeRange) RemoveOverlaps(timeRange TimeRange) []TimeRange {
	if !tr.OverlapsExclusive(timeRange) {
		return []TimeRange{*tr}
	}

	start1 := tr.Start.UnixNano()
	end1 := tr.End.UnixNano()
	start2 := timeRange.Start.UnixNano()
	end2 := timeRange.End.UnixNano()

	switch {
	case start2 <= start1 && end2 >= end1:
		return nil
	case start1 < start2 && end1 > end2:
		return []TimeRange{
			{Start: tr.Start, End: timeRange.Start},
			{Start: timeRange.End, End: tr.End},
		}
	case start1 < start2:
		return []TimeRange{
			{Start: tr.Start, End: timeRange.Start},
		}
	case end1 > end2:
		return []TimeRange{
			{Start: timeRange.End, End: tr.End},
		}
	default:
		panic(fmt.Sprintf("could not remove overlaps between time ranges %v, %v", *tr, timeRange))
	}
}

func (tr *TimeRange) TrimEnd(end time.Time) *TimeRange {
	if end.Before(tr.Start) || end.Equal(tr.Start) {
		return nil
	}

	newEnd := tr.End
	if end.Before(newEnd) {
		newEnd = end
	}

	return &TimeRange{
		Start: tr.Start,
		End:   newEnd,
	}
}

func (tr *TimeRange) String() string {
	return fmt.Sprintf("%s - %s", tr.Start.Format(time.RFC3339), tr.End.Format(time.RFC3339))
}
