package service

import (
	"context"
	"time"

	"eslogad-be/internal/appcontext"
	"eslogad-be/internal/contract"
	"eslogad-be/internal/repository"

	"github.com/google/uuid"
)

type AnalyticsService interface {
	GetDashboard(ctx context.Context, req *contract.AnalyticsDashboardRequest) (*contract.AnalyticsDashboardResponse, error)
	GetLetterVolume(ctx context.Context) (*contract.LetterVolumeByTypeResponse, error)
}

type AnalyticsServiceImpl struct {
	analyticsRepo *repository.AnalyticsRepository
}

func NewAnalyticsService(analyticsRepo *repository.AnalyticsRepository) *AnalyticsServiceImpl {
	return &AnalyticsServiceImpl{
		analyticsRepo: analyticsRepo,
	}
}

func (s *AnalyticsServiceImpl) GetDashboard(ctx context.Context, req *contract.AnalyticsDashboardRequest) (*contract.AnalyticsDashboardResponse, error) {
	// Parse dates
	var startDate, endDate time.Time
	if req.StartDate != "" {
		if date, err := time.Parse("2006-01-02", req.StartDate); err == nil {
			startDate = date
		}
	} else {
		// Default to last 30 days
		startDate = time.Now().AddDate(0, 0, -30)
	}
	
	if req.EndDate != "" {
		if date, err := time.Parse("2006-01-02", req.EndDate); err == nil {
			endDate = date.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
		}
	} else {
		endDate = time.Now()
	}

	// Apply user context filters if not admin
	var userID *uuid.UUID
	
	appCtx := appcontext.FromGinContext(ctx)
	if appCtx != nil && appCtx.UserRole != "admin" && appCtx.UserRole != "superadmin" {
		userID = &appCtx.UserID
	}

	response := &contract.AnalyticsDashboardResponse{}

	// Get summary statistics - don't filter by department for overall stats
	summaryData, err := s.analyticsRepo.GetLetterSummaryStats(ctx, startDate, endDate, userID, nil)
	if err != nil {
		return nil, err
	}
	response.Summary = s.mapSummaryStats(summaryData)
	
	// Calculate growth metrics
	response.Summary.WeekOverWeekGrowth = s.calculateWeekOverWeekGrowth(ctx)
	response.Summary.MonthOverMonthGrowth = s.calculateMonthOverMonthGrowth(ctx)

	// Get priority distribution
	priorityData, err := s.analyticsRepo.GetPriorityDistribution(ctx, startDate, endDate)
	if err != nil {
		return nil, err
	}
	response.PriorityDistribution = s.mapPriorityDistribution(priorityData)

	// Get department statistics
	deptData, err := s.analyticsRepo.GetDepartmentStats(ctx, startDate, endDate)
	if err != nil {
		return nil, err
	}
	response.DepartmentStats = s.mapDepartmentStats(deptData)

	// Get monthly trend (last 12 months)
	monthlyData, err := s.analyticsRepo.GetMonthlyTrend(ctx, 12)
	if err != nil {
		return nil, err
	}
	response.MonthlyTrend = s.mapMonthlyTrend(monthlyData)

	// Get simplified department stats (departments_stats)
	response.DepartmentsStats = s.getSimpleDepartmentStats(ctx, startDate, endDate)

	// Get institution statistics
	instData, err := s.analyticsRepo.GetInstitutionStats(ctx, startDate, endDate)
	if err != nil {
		return nil, err
	}
	response.InstitutionStats = s.mapInstitutionStats(instData)

	// Get daily activity (last 7 days)
	dailyData, err := s.analyticsRepo.GetDailyActivity(ctx, 7)
	if err != nil {
		return nil, err
	}
	response.DailyActivity = s.mapDailyActivity(dailyData)

	return response, nil
}

func (s *AnalyticsServiceImpl) GetLetterVolume(ctx context.Context) (*contract.LetterVolumeByTypeResponse, error) {
	// This would be implemented with specific queries for volume metrics
	// For now, returning a placeholder
	return &contract.LetterVolumeByTypeResponse{
		Incoming: contract.IncomingLetterVolume{
			Today:     0,
			ThisWeek:  0,
			ThisMonth: 0,
			ThisYear:  0,
			Total:     0,
		},
		Outgoing: contract.OutgoingLetterVolume{
			Today:     0,
			ThisWeek:  0,
			ThisMonth: 0,
			ThisYear:  0,
			Total:     0,
		},
	}, nil
}

// Helper functions to map repository data to contract types

func (s *AnalyticsServiceImpl) mapSummaryStats(data map[string]interface{}) contract.LetterSummaryStats {
	return contract.LetterSummaryStats{
		TotalIncoming: getInt64Value(data["total_incoming"]),
		TotalOutgoing: getInt64Value(data["total_outgoing"]),
	}
}

// calculateWeekOverWeekGrowth calculates the week over week growth rate
func (s *AnalyticsServiceImpl) calculateWeekOverWeekGrowth(ctx context.Context) float64 {
	// Get this week's data
	thisWeekStart := time.Now().AddDate(0, 0, -int(time.Now().Weekday()))
	thisWeekEnd := time.Now()
	
	// Get last week's data
	lastWeekStart := thisWeekStart.AddDate(0, 0, -7)
	lastWeekEnd := thisWeekStart.AddDate(0, 0, -1)
	
	thisWeekData, _ := s.analyticsRepo.GetLetterSummaryStats(ctx, thisWeekStart, thisWeekEnd, nil, nil)
	lastWeekData, _ := s.analyticsRepo.GetLetterSummaryStats(ctx, lastWeekStart, lastWeekEnd, nil, nil)
	
	thisWeekTotal := getInt64Value(thisWeekData["total_incoming"]) + getInt64Value(thisWeekData["total_outgoing"])
	lastWeekTotal := getInt64Value(lastWeekData["total_incoming"]) + getInt64Value(lastWeekData["total_outgoing"])
	
	if lastWeekTotal > 0 {
		return float64((thisWeekTotal - lastWeekTotal) * 100 / lastWeekTotal)
	}
	return 0
}

// calculateMonthOverMonthGrowth calculates the month over month growth rate
func (s *AnalyticsServiceImpl) calculateMonthOverMonthGrowth(ctx context.Context) float64 {
	// Get this month's data
	now := time.Now()
	thisMonthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
	thisMonthEnd := now
	
	// Get last month's data
	lastMonthStart := thisMonthStart.AddDate(0, -1, 0)
	lastMonthEnd := thisMonthStart.AddDate(0, 0, -1)
	
	thisMonthData, _ := s.analyticsRepo.GetLetterSummaryStats(ctx, thisMonthStart, thisMonthEnd, nil, nil)
	lastMonthData, _ := s.analyticsRepo.GetLetterSummaryStats(ctx, lastMonthStart, lastMonthEnd, nil, nil)
	
	thisMonthTotal := getInt64Value(thisMonthData["total_incoming"]) + getInt64Value(thisMonthData["total_outgoing"])
	lastMonthTotal := getInt64Value(lastMonthData["total_incoming"]) + getInt64Value(lastMonthData["total_outgoing"])
	
	if lastMonthTotal > 0 {
		return float64((thisMonthTotal - lastMonthTotal) * 100 / lastMonthTotal)
	}
	return 0
}

// getSimpleDepartmentStats gets simplified department statistics
func (s *AnalyticsServiceImpl) getSimpleDepartmentStats(ctx context.Context, startDate, endDate time.Time) []contract.SimpleDepartmentStats {
	// Get department stats with letter counts
	deptData, err := s.analyticsRepo.GetDepartmentStats(ctx, startDate, endDate)
	if err != nil {
		return []contract.SimpleDepartmentStats{}
	}
	
	result := make([]contract.SimpleDepartmentStats, 0, len(deptData))
	for _, item := range deptData {
		deptIDStr := getStringValue(item["department_id"])
		deptID, err := uuid.Parse(deptIDStr)
		if err != nil {
			continue
		}
		
		// Calculate total letter count (incoming + outgoing)
		letterCount := getInt64Value(item["incoming_count"]) + getInt64Value(item["outgoing_count"])
		
		result = append(result, contract.SimpleDepartmentStats{
			DepartmentID: deptID,
			Department:   getStringValue(item["department_name"]),
			LetterCount:  letterCount,
		})
	}
	
	return result
}

func (s *AnalyticsServiceImpl) mapStatusDistribution(data []map[string]interface{}) []contract.StatusDistribution {
	result := make([]contract.StatusDistribution, 0, len(data))
	for _, item := range data {
		result = append(result, contract.StatusDistribution{
			Status:     getStringValue(item["status"]),
			Count:      getInt64Value(item["count"]),
			Percentage: getFloat64Value(item["percentage"]),
			Type:       getStringValue(item["type"]),
		})
	}
	return result
}

func (s *AnalyticsServiceImpl) mapPriorityDistribution(data []map[string]interface{}) []contract.PriorityDistribution {
	result := make([]contract.PriorityDistribution, 0, len(data))
	for _, item := range data {
		result = append(result, contract.PriorityDistribution{
			PriorityID:      getStringValue(item["priority_id"]),
			PriorityName:    getStringValue(item["priority_name"]),
			Level:           getIntValue(item["level"]),
			Count:           getInt64Value(item["count"]),
			Percentage:      getFloat64Value(item["percentage"]),
			AvgResponseTime: getFloat64Value(item["avg_response_time"]),
		})
	}
	return result
}

func (s *AnalyticsServiceImpl) mapDepartmentStats(data []map[string]interface{}) []contract.DepartmentStats {
	result := make([]contract.DepartmentStats, 0, len(data))
	for _, item := range data {
		if deptID, err := uuid.Parse(getStringValue(item["department_id"])); err == nil {
			result = append(result, contract.DepartmentStats{
				DepartmentID:    deptID,
				DepartmentName:  getStringValue(item["department_name"]),
				DepartmentCode:  getStringValue(item["department_code"]),
				IncomingCount:   getInt64Value(item["incoming_count"]),
				OutgoingCount:   getInt64Value(item["outgoing_count"]),
				PendingCount:    getInt64Value(item["pending_count"]),
				AvgResponseTime: getFloat64Value(item["avg_response_time"]),
				CompletionRate:  getFloat64Value(item["completion_rate"]),
			})
		}
	}
	return result
}

func (s *AnalyticsServiceImpl) mapMonthlyTrend(data []map[string]interface{}) []contract.MonthlyTrend {
	result := make([]contract.MonthlyTrend, 0, len(data))
	for _, item := range data {
		result = append(result, contract.MonthlyTrend{
			Month:         getStringValue(item["month"]),
			Year:          getIntValue(item["year"]),
			IncomingCount: getInt64Value(item["incoming_count"]),
			OutgoingCount: getInt64Value(item["outgoing_count"]),
			TotalCount:    getInt64Value(item["total_count"]),
			GrowthRate:    getFloat64Value(item["growth_rate"]),
		})
	}
	return result
}

func (s *AnalyticsServiceImpl) mapTopUsers(data []map[string]interface{}) []contract.TopUserStats {
	result := make([]contract.TopUserStats, 0, len(data))
	for _, item := range data {
		if userID, err := uuid.Parse(getStringValue(item["user_id"])); err == nil {
			result = append(result, contract.TopUserStats{
				UserID:          userID,
				UserName:        getStringValue(item["user_name"]),
				UserEmail:       getStringValue(item["user_email"]),
				Department:      getStringValue(item["department"]),
				LetterCount:     getInt64Value(item["letter_count"]),
				AvgResponseTime: getFloat64Value(item["avg_response_time"]),
			})
		}
	}
	return result
}

func (s *AnalyticsServiceImpl) mapInstitutionStats(data []map[string]interface{}) []contract.InstitutionStats {
	result := make([]contract.InstitutionStats, 0, len(data))
	for _, item := range data {
		if instID, err := uuid.Parse(getStringValue(item["institution_id"])); err == nil {
			stat := contract.InstitutionStats{
				InstitutionID:   instID,
				InstitutionName: getStringValue(item["institution_name"]),
				InstitutionType: getStringValue(item["institution_type"]),
				IncomingCount:   getInt64Value(item["incoming_count"]),
				OutgoingCount:   getInt64Value(item["outgoing_count"]),
				TotalCount:      getInt64Value(item["total_count"]),
			}
			
			if lastActivity, ok := item["last_activity"].(time.Time); ok {
				stat.LastActivity = lastActivity
			}
			
			result = append(result, stat)
		}
	}
	return result
}

func (s *AnalyticsServiceImpl) mapApprovalMetrics(data map[string]interface{}) contract.ApprovalMetrics {
	return contract.ApprovalMetrics{
		TotalSubmitted:   getInt64Value(data["total_submitted"]),
		TotalApproved:    getInt64Value(data["total_approved"]),
		TotalRejected:    getInt64Value(data["total_rejected"]),
		TotalPending:     getInt64Value(data["total_pending"]),
		ApprovalRate:     getFloat64Value(data["approval_rate"]),
		RejectionRate:    getFloat64Value(data["rejection_rate"]),
		AvgApprovalTime:  getFloat64Value(data["avg_approval_time"]),
		AvgApprovalSteps: getFloat64Value(data["avg_approval_steps"]),
	}
}

func (s *AnalyticsServiceImpl) mapResponseTimeStats(data map[string]interface{}) contract.ResponseTimeStats {
	return contract.ResponseTimeStats{
		MinResponseTime:    getFloat64Value(data["min_response_time"]),
		MaxResponseTime:    getFloat64Value(data["max_response_time"]),
		AvgResponseTime:    getFloat64Value(data["avg_response_time"]),
		MedianResponseTime: getFloat64Value(data["median_response_time"]),
		P95ResponseTime:    getFloat64Value(data["p95_response_time"]),
		P99ResponseTime:    getFloat64Value(data["p99_response_time"]),
	}
}

func (s *AnalyticsServiceImpl) mapDailyActivity(data []map[string]interface{}) []contract.DailyActivity {
	result := make([]contract.DailyActivity, 0, len(data))
	for _, item := range data {
		activity := contract.DailyActivity{
			Date:          "", // Leave date empty as per requirement
			DayOfWeek:     getStringValue(item["day_of_week"]),
			IncomingCount: getInt64Value(item["incoming_count"]),
			OutgoingCount: getInt64Value(item["outgoing_count"]),
		}
		result = append(result, activity)
	}
	return result
}

// Helper functions to safely extract values from interface{}

func getStringValue(v interface{}) string {
	if v == nil {
		return ""
	}
	if str, ok := v.(string); ok {
		return str
	}
	return ""
}

func getInt64Value(v interface{}) int64 {
	if v == nil {
		return 0
	}
	switch val := v.(type) {
	case int64:
		return val
	case float64:
		return int64(val)
	case int:
		return int64(val)
	default:
		return 0
	}
}

func getIntValue(v interface{}) int {
	if v == nil {
		return 0
	}
	switch val := v.(type) {
	case int:
		return val
	case int64:
		return int(val)
	case float64:
		return int(val)
	default:
		return 0
	}
}

func getFloat64Value(v interface{}) float64 {
	if v == nil {
		return 0
	}
	switch val := v.(type) {
	case float64:
		return val
	case int64:
		return float64(val)
	case int:
		return float64(val)
	default:
		return 0
	}
}