package report

import (
	"context"
	"errors"
	"log/slog"
	"sort"
	"time"

	"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"

	// notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"

	// virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
)

var (
	ErrInvalidTimeRange      = errors.New("invalid time range - start time must be before end time")
	ErrInvalidReportCriteria = errors.New("invalid report criteria")
)

type Service struct {
	betStore          bet.BetStore
	walletStore       wallet.WalletStore
	transactionStore  transaction.TransactionStore
	branchStore       branch.BranchStore
	userStore         user.UserStore
	repo              repository.ReportRepository
	companyStore      company.CompanyStore
	virtulaGamesStore repository.VirtualGameRepository
	notificationStore repository.NotificationRepository
	logger            *slog.Logger
}

func NewService(
	betStore bet.BetStore,
	walletStore wallet.WalletStore,
	transactionStore transaction.TransactionStore,
	branchStore branch.BranchStore,
	userStore user.UserStore,
	repo repository.ReportRepository,
	companyStore company.CompanyStore,
	virtulaGamesStore repository.VirtualGameRepository,
	notificationStore repository.NotificationRepository,
	logger *slog.Logger,
) *Service {
	return &Service{
		betStore:          betStore,
		walletStore:       walletStore,
		transactionStore:  transactionStore,
		branchStore:       branchStore,
		userStore:         userStore,
		repo:              repo,
		companyStore:      companyStore,
		virtulaGamesStore: virtulaGamesStore,
		notificationStore: notificationStore,
		logger:            logger,
	}
}

// GetDashboardSummary returns comprehensive dashboard metrics
func (s *Service) GetDashboardSummary(ctx context.Context, filter domain.ReportFilter) (domain.DashboardSummary, error) {
	if err := validateTimeRange(filter); err != nil {
		return domain.DashboardSummary{}, err
	}

	var summary domain.DashboardSummary
	var err error

	// Get bets summary
	summary.TotalStakes, summary.TotalBets, summary.ActiveBets, summary.TotalWins, summary.TotalLosses, summary.WinBalance, err =
		s.betStore.GetBetSummary(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get bet summary", "error", err)
		return domain.DashboardSummary{}, err
	}

	// Get customer metrics
	summary.CustomerCount, summary.ActiveCustomers, summary.InactiveCustomers, err = s.userStore.GetCustomerCounts(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get customer counts", "error", err)
		return domain.DashboardSummary{}, err
	}

	// Get branch metrics
	summary.BranchesCount, summary.ActiveBranches, summary.InactiveBranches, err = s.branchStore.GetBranchCounts(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get branch counts", "error", err)
		return domain.DashboardSummary{}, err
	}

	// Get transaction metrics
	summary.TotalDeposits, summary.TotalWithdrawals, err = s.transactionStore.GetTransactionTotals(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get transaction totals", "error", err)
		return domain.DashboardSummary{}, err
	}

	// Get user role metrics
	summary.TotalCashiers, summary.ActiveCashiers, summary.InactiveCashiers, err = s.userStore.GetRoleCounts(ctx, string(domain.RoleCashier), filter)
	if err != nil {
		s.logger.Error("failed to get cashier counts", "error", err)
		return domain.DashboardSummary{}, err
	}

	summary.TotalManagers, summary.ActiveManagers, summary.InactiveManagers, err = s.userStore.GetRoleCounts(ctx, string(domain.RoleBranchManager), filter)
	if err != nil {
		s.logger.Error("failed to get manager counts", "error", err)
		return domain.DashboardSummary{}, err
	}

	summary.TotalAdmins, summary.ActiveAdmins, summary.InactiveAdmins, err = s.userStore.GetRoleCounts(ctx, string(domain.RoleAdmin), filter)
	if err != nil {
		s.logger.Error("failed to get admin counts", "error", err)
		return domain.DashboardSummary{}, err
	}

	// Get wallet metrics
	summary.TotalWallets, err = s.walletStore.GetTotalWallets(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get wallet counts", "error", err)
		return domain.DashboardSummary{}, err
	}

	// Get sport/game metrics
	summary.TotalGames, summary.ActiveGames, summary.InactiveGames, err = s.virtulaGamesStore.GetGameCounts(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get game counts", "error", err)
		return domain.DashboardSummary{}, err
	}

	// Get company metrics
	summary.TotalCompanies, summary.ActiveCompanies, summary.InactiveCompanies, err = s.companyStore.GetCompanyCounts(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get company counts", "error", err)
		return domain.DashboardSummary{}, err
	}

	// Get notification metrics
	summary.TotalNotifications, summary.ReadNotifications, summary.UnreadNotifications, err = s.notificationStore.GetNotificationCounts(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get notification counts", "error", err)
		return domain.DashboardSummary{}, err
	}

	// Calculate derived metrics
	if summary.TotalBets > 0 {
		summary.AverageStake = summary.TotalStakes / domain.Currency(summary.TotalBets)
		summary.WinRate = float64(summary.TotalWins) / float64(summary.TotalBets) * 100
		summary.Profit = summary.TotalStakes - summary.WinBalance
	}

	return summary, nil
}

// Getdomain.BetAnalysis returns detailed bet analysis
func (s *Service) GetBetAnalysis(ctx context.Context, filter domain.ReportFilter) ([]domain.BetAnalysis, error) {
	if err := validateTimeRange(filter); err != nil {
		return nil, err
	}

	// Get basic bet stats
	betStats, err := s.betStore.GetBetStats(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get bet stats", "error", err)
		return nil, err
	}

	// Get sport popularity
	sportPopularity, err := s.betStore.GetSportPopularity(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get sport popularity", "error", err)
		return nil, err
	}

	// Get market popularity
	marketPopularity, err := s.betStore.GetMarketPopularity(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get market popularity", "error", err)
		return nil, err
	}

	// Get extreme values
	extremeValues, err := s.betStore.GetExtremeValues(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get extreme values", "error", err)
		return nil, err
	}

	// Combine data into analysis
	var analysis []domain.BetAnalysis
	for _, stat := range betStats {
		a := domain.BetAnalysis{
			Date:         stat.Date,
			TotalBets:    stat.TotalBets,
			TotalStakes:  stat.TotalStakes,
			TotalWins:    stat.TotalWins,
			TotalPayouts: stat.TotalPayouts,
			Profit:       stat.TotalStakes - stat.TotalPayouts,
			AverageOdds:  stat.AverageOdds,
		}

		// Add sport popularity
		if sport, ok := sportPopularity[stat.Date]; ok {
			a.MostPopularSport = sport
		}

		// Add market popularity
		if market, ok := marketPopularity[stat.Date]; ok {
			a.MostPopularMarket = market
		}

		// Add extreme values
		if extremes, ok := extremeValues[stat.Date]; ok {
			a.HighestStake = extremes.HighestStake
			a.HighestPayout = extremes.HighestPayout
		}

		analysis = append(analysis, a)
	}

	// Sort by date
	sort.Slice(analysis, func(i, j int) bool {
		return analysis[i].Date.Before(analysis[j].Date)
	})

	return analysis, nil
}

// Getdomain.CustomerActivity returns customer activity report
func (s *Service) GetCustomerActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.CustomerActivity, error) {
	if err := validateTimeRange(filter); err != nil {
		return nil, err
	}

	// Get customer bet activity
	customerBets, err := s.betStore.GetCustomerBetActivity(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get customer bet activity", "error", err)
		return nil, err
	}

	// Get customer details
	customerDetails, err := s.userStore.GetCustomerDetails(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get customer details", "error", err)
		return nil, err
	}

	// Get customer preferences
	customerPrefs, err := s.betStore.GetCustomerPreferences(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get customer preferences", "error", err)
		return nil, err
	}

	// Combine data into activity report
	var activities []domain.CustomerActivity
	for _, bet := range customerBets {
		activity := domain.CustomerActivity{
			CustomerID:   bet.CustomerID,
			TotalBets:    bet.TotalBets,
			TotalStakes:  bet.TotalStakes,
			TotalWins:    bet.TotalWins,
			TotalPayouts: bet.TotalPayouts,
			Profit:       bet.TotalStakes - bet.TotalPayouts,
			FirstBetDate: bet.FirstBetDate,
			LastBetDate:  bet.LastBetDate,
			AverageStake: bet.TotalStakes / domain.Currency(bet.TotalBets),
			AverageOdds:  bet.AverageOdds,
		}

		// Add customer details
		if details, ok := customerDetails[bet.CustomerID]; ok {
			activity.CustomerName = details.Name
		}

		// Add preferences
		if prefs, ok := customerPrefs[bet.CustomerID]; ok {
			activity.FavoriteSport = prefs.FavoriteSport
			activity.FavoriteMarket = prefs.FavoriteMarket
		}

		// Calculate win rate
		if bet.TotalBets > 0 {
			activity.WinRate = float64(bet.TotalWins) / float64(bet.TotalBets) * 100
		}

		// Determine activity level
		activity.ActivityLevel = calculateActivityLevel(bet.TotalBets, bet.TotalStakes)

		activities = append(activities, activity)
	}

	// Sort by total stakes (descending)
	sort.Slice(activities, func(i, j int) bool {
		return activities[i].TotalStakes > activities[j].TotalStakes
	})

	return activities, nil
}

// Getdomain.BranchPerformance returns branch performance report
func (s *Service) GetBranchPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.BranchPerformance, error) {
	// Get branch bet activity
	branchBets, err := s.betStore.GetBranchBetActivity(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get branch bet activity", "error", err)
		return nil, err
	}

	// Get branch details
	branchDetails, err := s.branchStore.GetBranchDetails(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get branch details", "error", err)
		return nil, err
	}

	// Get branch transactions
	branchTransactions, err := s.transactionStore.GetBranchTransactionTotals(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get branch transactions", "error", err)
		return nil, err
	}

	// Get branch customer counts
	branchCustomers, err := s.userStore.GetBranchCustomerCounts(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get branch customer counts", "error", err)
		return nil, err
	}

	// Combine data into performance report
	var performances []domain.BranchPerformance
	for _, bet := range branchBets {
		performance := domain.BranchPerformance{
			BranchID:     bet.BranchID,
			TotalBets:    bet.TotalBets,
			TotalStakes:  bet.TotalStakes,
			TotalWins:    bet.TotalWins,
			TotalPayouts: bet.TotalPayouts,
			Profit:       bet.TotalStakes - bet.TotalPayouts,
		}

		// Add branch details
		if details, ok := branchDetails[bet.BranchID]; ok {
			performance.BranchName = details.Name
			performance.Location = details.Location
			performance.ManagerName = details.ManagerName
		}

		// Add transactions
		if transactions, ok := branchTransactions[bet.BranchID]; ok {
			performance.Deposits = transactions.Deposits
			performance.Withdrawals = transactions.Withdrawals
		}

		// Add customer counts
		if customers, ok := branchCustomers[bet.BranchID]; ok {
			performance.CustomerCount = customers
		}

		// Calculate metrics
		if bet.TotalBets > 0 {
			performance.WinRate = float64(bet.TotalWins) / float64(bet.TotalBets) * 100
			performance.AverageStake = bet.TotalStakes / domain.Currency(bet.TotalBets)
		}

		// Calculate performance score
		performance.PerformanceScore = calculatePerformanceScore(performance)

		performances = append(performances, performance)
	}

	// Sort by performance score (descending)
	sort.Slice(performances, func(i, j int) bool {
		return performances[i].PerformanceScore > performances[j].PerformanceScore
	})

	return performances, nil
}

// Getdomain.SportPerformance returns sport performance report
func (s *Service) GetSportPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.SportPerformance, error) {
	// Get sport bet activity
	sportBets, err := s.betStore.GetSportBetActivity(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get sport bet activity", "error", err)
		return nil, err
	}

	// Get sport details (names)
	sportDetails, err := s.betStore.GetSportDetails(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get sport details", "error", err)
		return nil, err
	}

	// Get sport market popularity
	sportMarkets, err := s.betStore.GetSportMarketPopularity(ctx, filter)
	if err != nil {
		s.logger.Error("failed to get sport market popularity", "error", err)
		return nil, err
	}

	// Combine data into performance report
	var performances []domain.SportPerformance
	for _, bet := range sportBets {
		performance := domain.SportPerformance{
			SportID:      bet.SportID,
			TotalBets:    bet.TotalBets,
			TotalStakes:  bet.TotalStakes,
			TotalWins:    bet.TotalWins,
			TotalPayouts: bet.TotalPayouts,
			Profit:       bet.TotalStakes - bet.TotalPayouts,
			AverageOdds:  bet.AverageOdds,
		}

		// Add sport details
		if details, ok := sportDetails[bet.SportID]; ok {
			performance.SportName = details
		}

		// Add market popularity
		if market, ok := sportMarkets[bet.SportID]; ok {
			performance.MostPopularMarket = market
		}

		// Calculate metrics
		if bet.TotalBets > 0 {
			performance.WinRate = float64(bet.TotalWins) / float64(bet.TotalBets) * 100
			performance.AverageStake = bet.TotalStakes / domain.Currency(bet.TotalBets)
		}

		performances = append(performances, performance)
	}

	// Sort by total stakes (descending) and assign popularity rank
	sort.Slice(performances, func(i, j int) bool {
		return performances[i].TotalStakes > performances[j].TotalStakes
	})

	for i := range performances {
		performances[i].PopularityRank = i + 1
	}

	return performances, nil
}

func (s *Service) GenerateReport(timeFrame domain.TimeFrame) (*domain.Report, error) {
	now := time.Now()
	var start, end time.Time

	switch timeFrame {
	case domain.Daily:
		start = now.AddDate(0, 0, -1)
		end = now
	case domain.Weekly:
		start = now.AddDate(0, 0, -7)
		end = now
	case domain.Monthly:
		start = now.AddDate(0, -1, 0)
		end = now
	}

	report, err := s.repo.GenerateReport(timeFrame, start, end)
	if err != nil {
		return nil, err
	}

	if err := s.repo.SaveReport(report); err != nil {
		return nil, err
	}

	return report, nil
}

// func (s *Service) GetCompanyPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.CompanyPerformance, error) {
// 	// Get company bet activity
// 	companyBets, err := s.betStore.GetCompanyBetActivity(ctx, filter)
// 	if err != nil {
// 		s.logger.Error("failed to get company bet activity", "error", err)
// 		return nil, err
// 	}

// 	// Get company details
// 	companyDetails, err := s.branchStore.GetCompanyDetails(ctx, filter)
// 	if err != nil {
// 		s.logger.Error("failed to get company details", "error", err)
// 		return nil, err
// 	}

// 	// Get company branches
// 	companyBranches, err := s.branchStore.GetCompanyBranchCounts(ctx, filter)
// 	if err != nil {
// 		s.logger.Error("failed to get company branch counts", "error", err)
// 		return nil, err
// 	}

// 	// Combine data into performance report
// 	var performances []domain.CompanyPerformance
// 	for _, bet := range companyBets {
// 		performance := domain.CompanyPerformance{
// 			CompanyID:    bet.CompanyID,
// 			TotalBets:    bet.TotalBets,
// 			TotalStakes:  bet.TotalStakes,
// 			TotalWins:    bet.TotalWins,
// 			TotalPayouts: bet.TotalPayouts,
// 			Profit:       bet.TotalStakes - bet.TotalPayouts,
// 		}

// 		// Add company details
// 		if details, ok := companyDetails[bet.CompanyID]; ok {
// 			performance.CompanyName = details.Name
// 			performance.ContactEmail = details.ContactEmail
// 		}

// 		// Add branch counts
// 		if branches, ok := companyBranches[bet.CompanyID]; ok {
// 			performance.TotalBranches = branches.Total
// 			performance.ActiveBranches = branches.Active
// 		}

// 		// Calculate metrics
// 		if bet.TotalBets > 0 {
// 			performance.WinRate = float64(bet.TotalWins) / float64(bet.TotalBets) * 100
// 			performance.AverageStake = bet.TotalStakes / domain.Currency(bet.TotalBets)
// 		}

// 		performances = append(performances, performance)
// 	}

// 	// Sort by profit (descending)
// 	sort.Slice(performances, func(i, j int) bool {
// 		return performances[i].Profit > performances[j].Profit
// 	})

// 	return performances, nil
// }

// GetCashierPerformance returns cashier performance report
// func (s *Service) GetCashierPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.CashierPerformance, error) {
// 	// Get cashier bet activity
// 	cashierBets, err := s.betStore.GetCashierBetActivity(ctx, filter)
// 	if err != nil {
// 		s.logger.Error("failed to get cashier bet activity", "error", err)
// 		return nil, err
// 	}

// 	// Get cashier details
// 	cashierDetails, err := s.userStore.GetCashierDetails(ctx, filter)
// 	if err != nil {
// 		s.logger.Error("failed to get cashier details", "error", err)
// 		return nil, err
// 	}

// 	// Get cashier transactions
// 	cashierTransactions, err := s.transactionStore.GetCashierTransactionTotals(ctx, filter)
// 	if err != nil {
// 		s.logger.Error("failed to get cashier transactions", "error", err)
// 		return nil, err
// 	}

// 	// Combine data into performance report
// 	var performances []domain.CashierPerformance
// 	for _, bet := range cashierBets {
// 		performance := domain.CashierPerformance{
// 			CashierID:    bet.CashierID,
// 			TotalBets:    bet.TotalBets,
// 			TotalStakes:  bet.TotalStakes,
// 			TotalWins:    bet.TotalWins,
// 			TotalPayouts: bet.TotalPayouts,
// 			Profit:       bet.TotalStakes - bet.TotalPayouts,
// 		}

// 		// Add cashier details
// 		if details, ok := cashierDetails[bet.CashierID]; ok {
// 			performance.CashierName = details.Name
// 			performance.BranchID = details.BranchID
// 			performance.BranchName = details.BranchName
// 		}

// 		// Add transactions
// 		if transactions, ok := cashierTransactions[bet.CashierID]; ok {
// 			performance.Deposits = transactions.Deposits
// 			performance.Withdrawals = transactions.Withdrawals
// 		}

// 		// Calculate metrics
// 		if bet.TotalBets > 0 {
// 			performance.WinRate = float64(bet.TotalWins) / float64(bet.TotalBets) * 100
// 			performance.AverageStake = bet.TotalStakes / domain.Currency(bet.TotalBets)
// 		}

// 		performances = append(performances, performance)
// 	}

// 	// Sort by total stakes (descending)
// 	sort.Slice(performances, func(i, j int) bool {
// 		return performances[i].TotalStakes > performances[j].TotalStakes
// 	})

// 	return performances, nil
// }

// GetNotificationReport returns notification statistics report
// func (s *Service) GetNotificationReport(ctx context.Context, filter domain.ReportFilter) (domain.NotificationReport, error) {
// 	// Get notification counts by type
// 	countsByType, err := s.notificationStore.GetNotificationCountsByType(ctx, filter)
// 	if err != nil {
// 		s.logger.Error("failed to get notification counts by type", "error", err)
// 		return domain.NotificationReport{}, err
// 	}

// 	// Get notification delivery stats
// 	deliveryStats, err := s.notificationStore.GetNotificationDeliveryStats(ctx, filter)
// 	if err != nil {
// 		s.logger.Error("failed to get notification delivery stats", "error", err)
// 		return domain.NotificationReport{}, err
// 	}

// 	// Get most active notification recipients
// 	activeRecipients, err := s.notificationStore.GetMostActiveNotificationRecipients(ctx, filter)
// 	if err != nil {
// 		s.logger.Error("failed to get active notification recipients", "error", err)
// 		return domain.NotificationReport{}, err
// 	}

// 	return domain.NotificationReport{
// 		CountsByType:     countsByType,
// 		DeliveryStats:    deliveryStats,
// 		ActiveRecipients: activeRecipients,
// 	}, nil
// }

// Helper functions
func validateTimeRange(filter domain.ReportFilter) error {
	if filter.StartTime.Valid && filter.EndTime.Valid {
		if filter.StartTime.Value.After(filter.EndTime.Value) {
			return ErrInvalidTimeRange
		}
	}
	return nil
}

func calculateActivityLevel(totalBets int64, totalStakes domain.Currency) string {
	switch {
	case totalBets > 100 || totalStakes > 10000:
		return "High"
	case totalBets > 50 || totalStakes > 5000:
		return "Medium"
	default:
		return "Low"
	}
}

func calculatePerformanceScore(perf domain.BranchPerformance) float64 {
	// Simple scoring algorithm - can be enhanced based on business rules
	profitScore := float64(perf.Profit) / 1000
	customerScore := float64(perf.CustomerCount) * 0.1
	betScore := float64(perf.TotalBets) * 0.01
	winRateScore := perf.WinRate * 0.1

	return profitScore + customerScore + betScore + winRateScore
}
