package admin

import (
	"context"
	"fmt"
	"time"

	"github.com/ultisuite/ulti-backend/internal/ai/cost"
)

type AIUsageSummary struct {
	CostTodayMicroEUR     int64              `json:"cost_today_micro_eur"`
	CostMonthMicroEUR     int64              `json:"cost_month_micro_eur"`
	CostTodayEUR          float64            `json:"cost_today_eur"`
	CostMonthEUR          float64            `json:"cost_month_eur"`
	Currency              string             `json:"currency"`
	DailySeries           []AIUsageDayPoint  `json:"daily_series"`
	TopUsers              []AIUsageTopUser   `json:"top_users"`
	TopModels             []AIUsageTopModel  `json:"top_models"`
	OrgPolicy             cost.EffectiveLimits `json:"org_policy"`
}

type AIUsageDayPoint struct {
	Date              string  `json:"date"`
	CostOrgMicroEUR   int64   `json:"cost_org_micro_eur"`
	CostUserMicroEUR  int64   `json:"cost_user_micro_eur"`
	CostOrgEUR        float64 `json:"cost_org_eur"`
	Requests          int     `json:"requests"`
}

type AIUsageTopUser struct {
	UserID            string  `json:"user_id"`
	Email             string  `json:"email"`
	DisplayName       string  `json:"display_name"`
	CostOrgMicroEUR   int64   `json:"cost_org_micro_eur"`
	CostOrgEUR        float64 `json:"cost_org_eur"`
}

type AIUsageTopModel struct {
	ModelID           string  `json:"model_id"`
	CostMicroEUR      int64   `json:"cost_micro_eur"`
	CostEUR           float64 `json:"cost_eur"`
	RequestCount      int     `json:"request_count"`
}

type AIUserUsageDetail struct {
	UserID        string             `json:"user_id"`
	Email         string             `json:"email"`
	DisplayName   string             `json:"display_name"`
	Summary       AIUsageSummary     `json:"summary"`
	Events        []AIUsageEventItem `json:"events"`
	EventsTotal   int                `json:"events_total"`
}

type AIUsageEventItem struct {
	ID                string  `json:"id"`
	CreatedAt         string  `json:"created_at"`
	Feature           string  `json:"feature"`
	ModelID           string  `json:"model_id"`
	ProviderID        string  `json:"provider_id"`
	BillingScope      string  `json:"billing_scope"`
	CostMicroEUR      int64   `json:"cost_micro_eur"`
	CostEUR           float64 `json:"cost_eur"`
	PromptTokens      int     `json:"prompt_tokens"`
	CompletionTokens  int     `json:"completion_tokens"`
	CachedInputTokens int     `json:"cached_input_tokens"`
	Estimated         bool    `json:"estimated"`
}

type AIPricingEntry struct {
	ModelID                    string  `json:"model_id"`
	ProviderType               string  `json:"provider_type"`
	InputMicroEURPerMTok       int64   `json:"input_micro_eur_per_mtok"`
	CachedInputMicroEURPerMTok int64   `json:"cached_input_micro_eur_per_mtok,omitempty"`
	OutputMicroEURPerMTok      int64   `json:"output_micro_eur_per_mtok"`
	ReasoningMicroEURPerMTok   int64   `json:"reasoning_micro_eur_per_mtok,omitempty"`
	InputEURPerMTok            float64 `json:"input_eur_per_mtok"`
	OutputEURPerMTok           float64 `json:"output_eur_per_mtok"`
}

type AICostPolicyEntry struct {
	ScopeType              string   `json:"scope_type"`
	ScopeID                *string  `json:"scope_id,omitempty"`
	ScopeName              string   `json:"scope_name,omitempty"`
	DailyLimitMicroEUR     *int64   `json:"daily_limit_micro_eur,omitempty"`
	MonthlyLimitMicroEUR   *int64   `json:"monthly_limit_micro_eur,omitempty"`
	DailyLimitEUR          *float64 `json:"daily_limit_eur,omitempty"`
	MonthlyLimitEUR        *float64 `json:"monthly_limit_eur,omitempty"`
	WarnThresholdPct       int      `json:"warn_threshold_pct"`
}

type putAIPricingRequest struct {
	Prices []AIPricingEntry `json:"prices"`
}

type putAICostPolicyRequest struct {
	DailyLimitEUR        *float64 `json:"daily_limit_eur"`
	MonthlyLimitEUR      *float64 `json:"monthly_limit_eur"`
	WarnThresholdPct     int      `json:"warn_threshold_pct"`
	DailyLimitMicroEUR   *int64   `json:"daily_limit_micro_eur,omitempty"`
	MonthlyLimitMicroEUR *int64   `json:"monthly_limit_micro_eur,omitempty"`
}

func (s *Service) GetAIUsageSummary(ctx context.Context, billingScope string) (AIUsageSummary, error) {
	today := time.Now().UTC().Truncate(24 * time.Hour)
	month := time.Date(today.Year(), today.Month(), 1, 0, 0, 0, 0, time.UTC)

	out := AIUsageSummary{
		Currency:    "EUR",
		DailySeries: []AIUsageDayPoint{},
		TopUsers:    []AIUsageTopUser{},
		TopModels:   []AIUsageTopModel{},
	}
	policySvc := cost.NewPolicyService(s.db)
	orgPolicy, _ := policySvc.GetOrgPolicy(ctx)
	out.OrgPolicy = orgPolicy

	var dailyOrg, dailyUser int64
	_ = s.db.QueryRow(ctx, `
		SELECT COALESCE(cost_micro_eur_org, 0), COALESCE(cost_micro_eur_user, 0)
		FROM ai_org_usage_daily WHERE usage_date = $1
	`, today).Scan(&dailyOrg, &dailyUser)

	var monthlyOrg int64
	_ = s.db.QueryRow(ctx, `
		SELECT COALESCE(cost_micro_eur_org, 0) FROM ai_org_usage_monthly WHERE usage_month = $1
	`, month).Scan(&monthlyOrg)

	if billingScope == "user" {
		out.CostTodayMicroEUR = dailyUser
	} else {
		out.CostTodayMicroEUR = dailyOrg
	}
	out.CostMonthMicroEUR = monthlyOrg
	out.CostTodayEUR = cost.MicroEURToEUR(out.CostTodayMicroEUR)
	out.CostMonthEUR = cost.MicroEURToEUR(out.CostMonthMicroEUR)

	rows, err := s.db.Query(ctx, `
		SELECT usage_date, cost_micro_eur_org, cost_micro_eur_user, requests
		FROM ai_org_usage_daily
		WHERE usage_date >= $1
		ORDER BY usage_date ASC
	`, today.AddDate(0, 0, -29))
	if err == nil {
		defer rows.Close()
		for rows.Next() {
			var pt AIUsageDayPoint
			var d time.Time
			if err := rows.Scan(&d, &pt.CostOrgMicroEUR, &pt.CostUserMicroEUR, &pt.Requests); err != nil {
				continue
			}
			pt.Date = d.Format("2006-01-02")
			pt.CostOrgEUR = cost.MicroEURToEUR(pt.CostOrgMicroEUR)
			out.DailySeries = append(out.DailySeries, pt)
		}
	}

	topUserRows, err := s.db.Query(ctx, `
		SELECT u.id::text, COALESCE(u.email, ''), COALESCE(u.display_name, ''),
			COALESCE(SUM(e.cost_micro_eur), 0)
		FROM ai_usage_events e
		JOIN users u ON u.id = e.user_id
		WHERE e.created_at >= $1 AND e.billing_scope = 'org'
		GROUP BY u.id, u.email, u.display_name
		ORDER BY SUM(e.cost_micro_eur) DESC
		LIMIT 10
	`, month)
	if err == nil {
		defer topUserRows.Close()
		for topUserRows.Next() {
			var u AIUsageTopUser
			if err := topUserRows.Scan(&u.UserID, &u.Email, &u.DisplayName, &u.CostOrgMicroEUR); err != nil {
				continue
			}
			u.CostOrgEUR = cost.MicroEURToEUR(u.CostOrgMicroEUR)
			out.TopUsers = append(out.TopUsers, u)
		}
	}

	topModelRows, err := s.db.Query(ctx, `
		SELECT model_id, COALESCE(SUM(cost_micro_eur), 0), COUNT(*)
		FROM ai_usage_events
		WHERE created_at >= $1 AND billing_scope = 'org'
		GROUP BY model_id
		ORDER BY SUM(cost_micro_eur) DESC
		LIMIT 10
	`, month)
	if err == nil {
		defer topModelRows.Close()
		for topModelRows.Next() {
			var m AIUsageTopModel
			if err := topModelRows.Scan(&m.ModelID, &m.CostMicroEUR, &m.RequestCount); err != nil {
				continue
			}
			m.CostEUR = cost.MicroEURToEUR(m.CostMicroEUR)
			out.TopModels = append(out.TopModels, m)
		}
	}

	return out, nil
}

func (s *Service) GetUserAIUsage(ctx context.Context, userID string, limit, offset int) (AIUserUsageDetail, error) {
	if limit <= 0 {
		limit = 50
	}
	var detail AIUserUsageDetail
	detail.Events = []AIUsageEventItem{}
	err := s.db.QueryRow(ctx, `
		SELECT id::text, COALESCE(email, ''), COALESCE(display_name, '')
		FROM users WHERE id = $1::uuid
	`, userID).Scan(&detail.UserID, &detail.Email, &detail.DisplayName)
	if err != nil {
		return detail, fmt.Errorf("user not found")
	}

	summary, _ := s.GetAIUsageSummary(ctx, "org")
	detail.Summary = summary

	_ = s.db.QueryRow(ctx, `
		SELECT COUNT(*) FROM ai_usage_events WHERE user_id = $1::uuid
	`, userID).Scan(&detail.EventsTotal)

	rows, err := s.db.Query(ctx, `
		SELECT id::text, created_at, feature, model_id, provider_id, billing_scope,
			cost_micro_eur, prompt_tokens, completion_tokens, cached_input_tokens, estimated
		FROM ai_usage_events
		WHERE user_id = $1::uuid
		ORDER BY created_at DESC
		LIMIT $2 OFFSET $3
	`, userID, limit, offset)
	if err != nil {
		return detail, err
	}
	defer rows.Close()
	for rows.Next() {
		var ev AIUsageEventItem
		var created time.Time
		if err := rows.Scan(&ev.ID, &created, &ev.Feature, &ev.ModelID, &ev.ProviderID,
			&ev.BillingScope, &ev.CostMicroEUR, &ev.PromptTokens, &ev.CompletionTokens,
			&ev.CachedInputTokens, &ev.Estimated); err != nil {
			continue
		}
		ev.CreatedAt = created.UTC().Format(time.RFC3339)
		ev.CostEUR = cost.MicroEURToEUR(ev.CostMicroEUR)
		detail.Events = append(detail.Events, ev)
	}
	return detail, rows.Err()
}

func (s *Service) ListAIPricing(ctx context.Context) ([]AIPricingEntry, error) {
	store := cost.NewPricingStore(s.db)
	prices, err := store.ListPrices(ctx)
	if err != nil {
		return []AIPricingEntry{}, err
	}
	out := make([]AIPricingEntry, 0, len(prices))
	for _, p := range prices {
		out = append(out, AIPricingEntry{
			ModelID:                    p.ModelID,
			ProviderType:               p.ProviderType,
			InputMicroEURPerMTok:       p.InputMicroEURPerMTok,
			CachedInputMicroEURPerMTok: p.CachedInputMicroEURPerMTok,
			OutputMicroEURPerMTok:      p.OutputMicroEURPerMTok,
			ReasoningMicroEURPerMTok:   p.ReasoningMicroEURPerMTok,
			InputEURPerMTok:            cost.MicroEURToEUR(p.InputMicroEURPerMTok),
			OutputEURPerMTok:           cost.MicroEURToEUR(p.OutputMicroEURPerMTok),
		})
	}
	return out, nil
}

func (s *Service) PutAIPricing(ctx context.Context, actorSub string, req putAIPricingRequest) ([]AIPricingEntry, error) {
	store := cost.NewPricingStore(s.db)
	for _, p := range req.Prices {
		if err := store.UpsertModelPrice(ctx, cost.ModelPrice{
			ModelID:                    p.ModelID,
			ProviderType:               p.ProviderType,
			InputMicroEURPerMTok:       p.InputMicroEURPerMTok,
			CachedInputMicroEURPerMTok: p.CachedInputMicroEURPerMTok,
			OutputMicroEURPerMTok:      p.OutputMicroEURPerMTok,
			ReasoningMicroEURPerMTok:   p.ReasoningMicroEURPerMTok,
		}); err != nil {
			return nil, err
		}
	}
	s.logAudit(ctx, actorSub, "update_ai_pricing", map[string]any{"count": len(req.Prices)})
	return s.ListAIPricing(ctx)
}

func (s *Service) ListAICostPolicies(ctx context.Context) ([]AICostPolicyEntry, error) {
	rows, err := s.db.Query(ctx, `
		SELECT p.scope_type, p.scope_id::text, p.daily_limit_micro_eur, p.monthly_limit_micro_eur, p.warn_threshold_pct,
			COALESCE(g.name, u.email, 'Organisation')
		FROM ai_cost_policies p
		LEFT JOIN user_groups g ON p.scope_type = 'group' AND g.id = p.scope_id
		LEFT JOIN users u ON p.scope_type = 'user' AND u.id = p.scope_id
		ORDER BY p.priority ASC, p.scope_type
	`)
	if err != nil {
		return []AICostPolicyEntry{}, err
	}
	defer rows.Close()
	var out = make([]AICostPolicyEntry, 0)
	for rows.Next() {
		var e AICostPolicyEntry
		var scopeID *string
		if err := rows.Scan(&e.ScopeType, &scopeID, &e.DailyLimitMicroEUR, &e.MonthlyLimitMicroEUR,
			&e.WarnThresholdPct, &e.ScopeName); err != nil {
			continue
		}
		e.ScopeID = scopeID
		if e.DailyLimitMicroEUR != nil {
			v := cost.MicroEURToEUR(*e.DailyLimitMicroEUR)
			e.DailyLimitEUR = &v
		}
		if e.MonthlyLimitMicroEUR != nil {
			v := cost.MicroEURToEUR(*e.MonthlyLimitMicroEUR)
			e.MonthlyLimitEUR = &v
		}
		out = append(out, e)
	}
	return out, rows.Err()
}

func (s *Service) PutUserAICostPolicy(ctx context.Context, actorSub, userID string, req putAICostPolicyRequest) error {
	daily, monthly := resolveCostLimits(req)
	policy := cost.NewPolicyService(s.db)
	if err := policy.UpsertScopePolicy(ctx, "user", userID, daily, monthly, req.WarnThresholdPct); err != nil {
		return err
	}
	s.logAudit(ctx, actorSub, "set_user_ai_cost_policy", map[string]any{"user_id": userID})
	return nil
}

func (s *Service) PutGroupAICostPolicy(ctx context.Context, actorSub, groupID string, req putAICostPolicyRequest) error {
	daily, monthly := resolveCostLimits(req)
	policy := cost.NewPolicyService(s.db)
	if err := policy.UpsertScopePolicy(ctx, "group", groupID, daily, monthly, req.WarnThresholdPct); err != nil {
		return err
	}
	s.logAudit(ctx, actorSub, "set_group_ai_cost_policy", map[string]any{"group_id": groupID})
	return nil
}

func (s *Service) syncUsageQuotasToCostPolicy(ctx context.Context, usageQuotas map[string]any) error {
	if usageQuotas == nil {
		return nil
	}
	var daily, monthly *int64
	warnPct := 80

	if v, ok := usageQuotas["llm_daily_cost_limit_eur"].(float64); ok && v > 0 {
		m := int64(v * 1_000_000)
		daily = &m
	}
	if v, ok := usageQuotas["llm_monthly_cost_limit_eur"].(float64); ok && v > 0 {
		m := int64(v * 1_000_000)
		monthly = &m
	}
	if v, ok := usageQuotas["llm_cost_warn_threshold_pct"].(float64); ok && v > 0 {
		warnPct = int(v)
	}

	policy := cost.NewPolicyService(s.db)
	return policy.UpsertOrgPolicy(ctx, daily, monthly, warnPct)
}

func resolveCostLimits(req putAICostPolicyRequest) (*int64, *int64) {
	var daily, monthly *int64
	if req.DailyLimitMicroEUR != nil {
		daily = req.DailyLimitMicroEUR
	} else if req.DailyLimitEUR != nil && *req.DailyLimitEUR > 0 {
		v := int64(*req.DailyLimitEUR * 1_000_000)
		daily = &v
	}
	if req.MonthlyLimitMicroEUR != nil {
		monthly = req.MonthlyLimitMicroEUR
	} else if req.MonthlyLimitEUR != nil && *req.MonthlyLimitEUR > 0 {
		v := int64(*req.MonthlyLimitEUR * 1_000_000)
		monthly = &v
	}
	return daily, monthly
}
