package service

import (
	"context"
	"fmt"
	"time"

	"oci-portal/internal/model"
)

// OverviewTenants 是租户数量与类别分布。
type OverviewTenants struct {
	Total  int            `json:"total"`
	Alive  int            `json:"alive"`
	Dead   int            `json:"dead"`
	ByType map[string]int `json:"byType"`
}

// OverviewCheck 是测活快照聚合；实例数为各配置默认区域之和，
// CoveredConfigs/TotalConfigs 用于前端标注覆盖度。
type OverviewCheck struct {
	HasActiveTask  bool       `json:"hasActiveTask"`
	CoveredConfigs int        `json:"coveredConfigs"`
	TotalConfigs   int        `json:"totalConfigs"`
	InstanceCount  int        `json:"instanceCount"`
	LastCheckedAt  *time.Time `json:"lastCheckedAt"`
}

// OverviewCostDay 是一天的跨租户成本合计。
type OverviewCostDay struct {
	Day    string  `json:"day"`
	Amount float64 `json:"amount"`
}

// OverviewCost 是近 7 日成本快照聚合；金额跨配置直接相加，
// Currency 取快照中出现的第一个币种（混合币种时以此为准展示）。
type OverviewCost struct {
	HasActiveTask  bool              `json:"hasActiveTask"`
	CoveredConfigs int               `json:"coveredConfigs"`
	Currency       string            `json:"currency"`
	Total          float64           `json:"total"`
	Days           []OverviewCostDay `json:"days"`
}

// OverviewTasks 是后台任务数量统计。
type OverviewTasks struct {
	Total       int `json:"total"`
	Active      int `json:"active"`
	HealthCheck int `json:"healthCheck"`
	Cost        int `json:"cost"`
	Snatch      int `json:"snatch"`
}

// Overview 是总览页聚合数据。
type Overview struct {
	Tenants OverviewTenants `json:"tenants"`
	Check   OverviewCheck   `json:"check"`
	Cost    OverviewCost    `json:"cost"`
	Tasks   OverviewTasks   `json:"tasks"`
}

// Overview 聚合总览数据：租户分布、测活/成本快照与任务统计，全部读本地库不发云端请求。
func (s *OciConfigService) Overview(ctx context.Context) (*Overview, error) {
	var out Overview
	if err := s.overviewTenants(ctx, &out); err != nil {
		return nil, err
	}
	if err := s.overviewCheck(ctx, &out); err != nil {
		return nil, err
	}
	if err := s.overviewCost(ctx, &out); err != nil {
		return nil, err
	}
	if err := s.overviewTasks(ctx, &out); err != nil {
		return nil, err
	}
	return &out, nil
}

func (s *OciConfigService) overviewTenants(ctx context.Context, out *Overview) error {
	configs, err := s.List(ctx)
	if err != nil {
		return err
	}
	out.Tenants.Total = len(configs)
	out.Tenants.ByType = map[string]int{}
	for _, cfg := range configs {
		out.Tenants.ByType[cfg.AccountType]++
		switch cfg.AliveStatus {
		case model.AliveStatusAlive:
			out.Tenants.Alive++
		case model.AliveStatusDead, model.AliveStatusSuspended:
			out.Tenants.Dead++
		}
	}
	return nil
}

// hasActiveTask 判断是否存在指定类型且调度中的任务。
func (s *OciConfigService) hasActiveTask(ctx context.Context, taskType string) (bool, error) {
	var n int64
	err := s.db.WithContext(ctx).Model(&model.Task{}).
		Where("type = ? AND status = ?", taskType, model.TaskStatusActive).Count(&n).Error
	if err != nil {
		return false, fmt.Errorf("count %s tasks: %w", taskType, err)
	}
	return n > 0, nil
}

func (s *OciConfigService) overviewCheck(ctx context.Context, out *Overview) error {
	has, err := s.hasActiveTask(ctx, model.TaskTypeHealthCheck)
	if err != nil {
		return err
	}
	out.Check.HasActiveTask = has
	out.Check.TotalConfigs = out.Tenants.Total
	var snaps []model.CheckSnapshot
	err = s.db.WithContext(ctx).
		Where("oci_config_id IN (?)", s.db.Model(&model.OciConfig{}).Select("id")).
		Find(&snaps).Error
	if err != nil {
		return fmt.Errorf("load check snapshots: %w", err)
	}
	out.Check.CoveredConfigs = len(snaps)
	for _, snap := range snaps {
		out.Check.InstanceCount += snap.InstanceCount
		if out.Check.LastCheckedAt == nil || snap.CheckedAt.After(*out.Check.LastCheckedAt) {
			t := snap.CheckedAt
			out.Check.LastCheckedAt = &t
		}
	}
	return nil
}

func (s *OciConfigService) overviewCost(ctx context.Context, out *Overview) error {
	has, err := s.hasActiveTask(ctx, model.TaskTypeCost)
	if err != nil {
		return err
	}
	out.Cost.HasActiveTask = has
	since := time.Now().UTC().AddDate(0, 0, -7).Format("2006-01-02")
	var snaps []model.CostSnapshot
	err = s.db.WithContext(ctx).
		Where("day >= ? AND oci_config_id IN (?)", since, s.db.Model(&model.OciConfig{}).Select("id")).
		Order("day").Find(&snaps).Error
	if err != nil {
		return fmt.Errorf("load cost snapshots: %w", err)
	}
	aggregateCostDays(&out.Cost, snaps)
	return nil
}

// aggregateCostDays 把逐配置逐日的快照聚合为跨租户的每日序列（snaps 须按 day 升序）。
func aggregateCostDays(cost *OverviewCost, snaps []model.CostSnapshot) {
	byDay := map[string]float64{}
	covered := map[uint]bool{}
	var order []string
	for _, snap := range snaps {
		if _, ok := byDay[snap.Day]; !ok {
			order = append(order, snap.Day)
		}
		byDay[snap.Day] += snap.Amount
		covered[snap.OciConfigID] = true
		if cost.Currency == "" {
			cost.Currency = snap.Currency
		}
	}
	cost.CoveredConfigs = len(covered)
	cost.Days = make([]OverviewCostDay, 0, len(order))
	for _, day := range order {
		cost.Days = append(cost.Days, OverviewCostDay{Day: day, Amount: byDay[day]})
		cost.Total += byDay[day]
	}
}

func (s *OciConfigService) overviewTasks(ctx context.Context, out *Overview) error {
	var tasks []model.Task
	if err := s.db.WithContext(ctx).Find(&tasks).Error; err != nil {
		return fmt.Errorf("list tasks: %w", err)
	}
	out.Tasks.Total = len(tasks)
	for _, t := range tasks {
		if t.Status == model.TaskStatusActive {
			out.Tasks.Active++
		}
		switch t.Type {
		case model.TaskTypeHealthCheck:
			out.Tasks.HealthCheck++
		case model.TaskTypeCost:
			out.Tasks.Cost++
		case model.TaskTypeSnatch:
			out.Tasks.Snatch++
		}
	}
	return nil
}
