package service

import (
	"context"
	"fmt"
	"time"

	"oci-portal/internal/oci"
)

// maxUsageWindowDays 限制流量/成本查询窗口，避免拉取超大区间。
const maxUsageWindowDays = 366

// InstanceTraffic 查询实例近 days 天的进出流量（按天聚合）。
func (s *OciConfigService) InstanceTraffic(ctx context.Context, id uint, region, instanceID string, days int) (oci.InstanceTraffic, error) {
	if days <= 0 {
		days = 30
	}
	if days > maxUsageWindowDays {
		return oci.InstanceTraffic{}, fmt.Errorf("instance traffic: days must be within %d", maxUsageWindowDays)
	}
	cred, err := s.credentialsByID(ctx, id)
	if err != nil {
		return oci.InstanceTraffic{}, err
	}
	end := time.Now().UTC().Truncate(time.Hour).Add(time.Hour)
	return s.client.SummarizeInstanceTraffic(ctx, cred, oci.TrafficQuery{
		Region:     region,
		InstanceID: instanceID,
		StartTime:  end.AddDate(0, 0, -days),
		EndTime:    end,
	})
}

// Costs 汇总租户成本或用量；时间缺省为最近 30 天，起止按粒度对齐。
func (s *OciConfigService) Costs(ctx context.Context, id uint, q oci.CostQuery) ([]oci.CostItem, error) {
	applyCostDefaults(&q)
	if q.EndTime.Sub(q.StartTime) <= 0 {
		return nil, fmt.Errorf("costs: endTime must be after startTime")
	}
	if q.EndTime.Sub(q.StartTime) > maxUsageWindowDays*24*time.Hour {
		return nil, fmt.Errorf("costs: window must be within %d days", maxUsageWindowDays)
	}
	cred, err := s.credentialsByID(ctx, id)
	if err != nil {
		return nil, err
	}
	return s.client.SummarizeCosts(ctx, cred, q)
}

// applyCostDefaults 填充缺省值并把时间对齐到粒度边界（Usage API 要求）。
func applyCostDefaults(q *oci.CostQuery) {
	if q.Granularity == "" {
		q.Granularity = "DAILY"
	}
	if q.QueryType == "" {
		q.QueryType = "COST"
	}
	if q.GroupBy == "" {
		q.GroupBy = "service"
	}
	now := time.Now().UTC()
	if q.EndTime.IsZero() {
		q.EndTime = now
	}
	if q.StartTime.IsZero() {
		q.StartTime = q.EndTime.AddDate(0, 0, -30)
	}
	q.StartTime = alignDown(q.StartTime, q.Granularity)
	// endTime 为开区间，向上对齐到粒度边界以覆盖当天/当月
	q.EndTime = alignUp(q.EndTime, q.Granularity)
}

func alignDown(t time.Time, granularity string) time.Time {
	t = t.UTC()
	if granularity == "MONTHLY" {
		return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.UTC)
	}
	return t.Truncate(24 * time.Hour)
}

func alignUp(t time.Time, granularity string) time.Time {
	down := alignDown(t, granularity)
	if down.Equal(t.UTC()) {
		return down
	}
	if granularity == "MONTHLY" {
		return down.AddDate(0, 1, 0)
	}
	return down.AddDate(0, 0, 1)
}
