package handlers

import (
	"net/http"
	"sync"
	"time"

	"github.com/labstack/echo/v4"
	"gorm.io/gorm"

	"github.com/treytartt/honeydue-api/internal/models"
	"github.com/treytartt/honeydue-api/internal/task/scopes"
)

// dashboardCache provides short-lived in-memory caching for dashboard stats
// to avoid expensive COUNT queries on every request.
type dashboardCache struct {
	mu        sync.RWMutex
	stats     *DashboardStats
	expiresAt time.Time
}

var dashCache = &dashboardCache{}

const dashboardCacheTTL = 30 * time.Second

// AdminDashboardHandler handles admin dashboard endpoints
type AdminDashboardHandler struct {
	db *gorm.DB
}

// NewAdminDashboardHandler creates a new admin dashboard handler
func NewAdminDashboardHandler(db *gorm.DB) *AdminDashboardHandler {
	return &AdminDashboardHandler{db: db}
}

// DashboardStats holds all dashboard statistics
type DashboardStats struct {
	Users         UserStats         `json:"users"`
	Residences    ResidenceStats    `json:"residences"`
	Tasks         TaskStats         `json:"tasks"`
	Contractors   ContractorStats   `json:"contractors"`
	Documents     DocumentStats     `json:"documents"`
	Notifications NotificationStats `json:"notifications"`
	Subscriptions SubscriptionStats `json:"subscriptions"`
}

// UserStats holds user-related statistics
type UserStats struct {
	Total    int64 `json:"total"`
	Active   int64 `json:"active"`
	Verified int64 `json:"verified"`
	New30d   int64 `json:"new_30d"`
}

// ResidenceStats holds residence-related statistics
type ResidenceStats struct {
	Total   int64 `json:"total"`
	Active  int64 `json:"active"`
	New30d  int64 `json:"new_30d"`
}

// TaskStats holds task-related statistics
type TaskStats struct {
	Total      int64 `json:"total"`
	Active     int64 `json:"active"`      // Non-archived, non-cancelled
	Archived   int64 `json:"archived"`
	Cancelled  int64 `json:"cancelled"`
	Overdue    int64 `json:"overdue"`
	New30d     int64 `json:"new_30d"`
	// By status
	Pending    int64 `json:"pending"`
	InProgress int64 `json:"in_progress"`
	Completed  int64 `json:"completed"`
	OnHold     int64 `json:"on_hold"`
}

// ContractorStats holds contractor-related statistics
type ContractorStats struct {
	Total    int64 `json:"total"`
	Active   int64 `json:"active"`
	Favorite int64 `json:"favorite"`
}

// DocumentStats holds document-related statistics
type DocumentStats struct {
	Total   int64 `json:"total"`
	Active  int64 `json:"active"`
	Expired int64 `json:"expired"`
}

// NotificationStats holds notification-related statistics
type NotificationStats struct {
	Total   int64 `json:"total"`
	Sent    int64 `json:"sent"`
	Pending int64 `json:"pending"`
	Read    int64 `json:"read"`
}

// SubscriptionStats holds subscription-related statistics
type SubscriptionStats struct {
	Total   int64 `json:"total"`
	Free    int64 `json:"free"`
	Premium int64 `json:"premium"`
	Pro     int64 `json:"pro"`
}

// GetStats handles GET /api/admin/dashboard/stats
func (h *AdminDashboardHandler) GetStats(c echo.Context) error {
	// Check cache first
	dashCache.mu.RLock()
	if dashCache.stats != nil && time.Now().Before(dashCache.expiresAt) {
		cached := *dashCache.stats
		dashCache.mu.RUnlock()
		return c.JSON(http.StatusOK, cached)
	}
	dashCache.mu.RUnlock()

	stats := DashboardStats{}
	now := time.Now()
	thirtyDaysAgo := now.AddDate(0, 0, -30)

	// User stats
	h.db.Model(&models.User{}).Count(&stats.Users.Total)
	h.db.Model(&models.User{}).Where("is_active = ?", true).Count(&stats.Users.Active)
	h.db.Model(&models.UserProfile{}).Where("verified = ?", true).Count(&stats.Users.Verified)
	h.db.Model(&models.User{}).Where("date_joined >= ?", thirtyDaysAgo).Count(&stats.Users.New30d)

	// Residence stats
	h.db.Model(&models.Residence{}).Count(&stats.Residences.Total)
	h.db.Model(&models.Residence{}).Where("is_active = ?", true).Count(&stats.Residences.Active)
	h.db.Model(&models.Residence{}).Where("created_at >= ?", thirtyDaysAgo).Count(&stats.Residences.New30d)

	// Task stats - uses scopes from internal/task/scopes for consistency
	h.db.Model(&models.Task{}).Count(&stats.Tasks.Total)
	h.db.Model(&models.Task{}).Scopes(scopes.ScopeActive).Count(&stats.Tasks.Active)
	h.db.Model(&models.Task{}).Scopes(scopes.ScopeArchived).Count(&stats.Tasks.Archived)
	h.db.Model(&models.Task{}).Scopes(scopes.ScopeCancelled).Count(&stats.Tasks.Cancelled)
	h.db.Model(&models.Task{}).Where("created_at >= ?", thirtyDaysAgo).Count(&stats.Tasks.New30d)

	// Task counts by in_progress flag
	// Pending: active tasks that are not in progress and not completed
	h.db.Model(&models.Task{}).
		Scopes(scopes.ScopeActive, scopes.ScopeNotInProgress, scopes.ScopeNotCompleted).
		Count(&stats.Tasks.Pending)

	// In Progress: active tasks with in_progress = true
	h.db.Model(&models.Task{}).
		Scopes(scopes.ScopeActive, scopes.ScopeInProgress).
		Count(&stats.Tasks.InProgress)

	// Completed count: uses kanban completion logic (NextDueDate == nil AND has completions)
	// See internal/task/predicates.IsCompleted for the definition
	h.db.Model(&models.Task{}).
		Scopes(scopes.ScopeActive, scopes.ScopeCompleted).
		Count(&stats.Tasks.Completed)

	// OnHold: no longer used with in_progress boolean, set to 0
	stats.Tasks.OnHold = 0

	// Overdue: uses consistent logic from internal/task/scopes.ScopeOverdue
	// Effective date (COALESCE(next_due_date, due_date)) < now, active, not completed
	h.db.Model(&models.Task{}).
		Scopes(scopes.ScopeOverdue(now)).
		Count(&stats.Tasks.Overdue)

	// Contractor stats
	h.db.Model(&models.Contractor{}).Count(&stats.Contractors.Total)
	h.db.Model(&models.Contractor{}).Where("is_active = ?", true).Count(&stats.Contractors.Active)
	h.db.Model(&models.Contractor{}).Where("is_favorite = ?", true).Count(&stats.Contractors.Favorite)

	// Document stats
	h.db.Model(&models.Document{}).Count(&stats.Documents.Total)
	h.db.Model(&models.Document{}).Where("is_active = ?", true).Count(&stats.Documents.Active)
	h.db.Model(&models.Document{}).Where("expiry_date < ? AND is_active = ?", now, true).Count(&stats.Documents.Expired)

	// Notification stats
	h.db.Model(&models.Notification{}).Count(&stats.Notifications.Total)
	h.db.Model(&models.Notification{}).Where("sent = ?", true).Count(&stats.Notifications.Sent)
	h.db.Model(&models.Notification{}).Where("sent = ?", false).Count(&stats.Notifications.Pending)
	h.db.Model(&models.Notification{}).Where("read = ?", true).Count(&stats.Notifications.Read)

	// Subscription stats
	h.db.Model(&models.UserSubscription{}).Count(&stats.Subscriptions.Total)
	h.db.Model(&models.UserSubscription{}).Where("tier = ?", "free").Count(&stats.Subscriptions.Free)
	h.db.Model(&models.UserSubscription{}).Where("tier = ?", "premium").Count(&stats.Subscriptions.Premium)
	h.db.Model(&models.UserSubscription{}).Where("tier = ?", "pro").Count(&stats.Subscriptions.Pro)

	// Cache the result
	dashCache.mu.Lock()
	dashCache.stats = &stats
	dashCache.expiresAt = time.Now().Add(dashboardCacheTTL)
	dashCache.mu.Unlock()

	return c.JSON(http.StatusOK, stats)
}
