package handlers

import (
	"net/http"
	"strconv"

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

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

// AdminOnboardingHandler handles admin onboarding email operations
type AdminOnboardingHandler struct {
	db                  *gorm.DB
	onboardingService   *services.OnboardingEmailService
}

// NewAdminOnboardingHandler creates a new admin onboarding handler
func NewAdminOnboardingHandler(db *gorm.DB, onboardingService *services.OnboardingEmailService) *AdminOnboardingHandler {
	return &AdminOnboardingHandler{
		db:                db,
		onboardingService: onboardingService,
	}
}

// OnboardingEmailResponse represents an onboarding email in API responses
type OnboardingEmailResponse struct {
	ID         uint    `json:"id"`
	UserID     uint    `json:"user_id"`
	UserEmail  string  `json:"user_email,omitempty"`
	UserName   string  `json:"user_name,omitempty"`
	EmailType  string  `json:"email_type"`
	SentAt     string  `json:"sent_at"`
	OpenedAt   *string `json:"opened_at,omitempty"`
	TrackingID string  `json:"tracking_id"`
	CreatedAt  string  `json:"created_at"`
}

// OnboardingEmailListResponse is the paginated list response
type OnboardingEmailListResponse struct {
	Data       []OnboardingEmailResponse `json:"data"`
	Total      int64                     `json:"total"`
	Page       int                       `json:"page"`
	PageSize   int                       `json:"page_size"`
	TotalPages int                       `json:"total_pages"`
}

// OnboardingStatsResponse contains email statistics
type OnboardingStatsResponse struct {
	NoResidenceTotal  int64   `json:"no_residence_total"`
	NoResidenceOpened int64   `json:"no_residence_opened"`
	NoResidenceRate   float64 `json:"no_residence_open_rate"`
	NoTasksTotal      int64   `json:"no_tasks_total"`
	NoTasksOpened     int64   `json:"no_tasks_opened"`
	NoTasksRate       float64 `json:"no_tasks_open_rate"`
	TotalSent         int64   `json:"total_sent"`
	TotalOpened       int64   `json:"total_opened"`
	OverallRate       float64 `json:"overall_open_rate"`
}

// List returns paginated list of onboarding emails
// GET /api/admin/onboarding-emails
func (h *AdminOnboardingHandler) List(c echo.Context) error {
	pageStr := c.QueryParam("page")
	if pageStr == "" {
		pageStr = "1"
	}
	page, _ := strconv.Atoi(pageStr)

	pageSizeStr := c.QueryParam("page_size")
	if pageSizeStr == "" {
		pageSizeStr = "20"
	}
	pageSize, _ := strconv.Atoi(pageSizeStr)
	emailType := c.QueryParam("email_type")
	userID, _ := strconv.Atoi(c.QueryParam("user_id"))
	opened := c.QueryParam("opened")

	if page < 1 {
		page = 1
	}
	if pageSize < 1 || pageSize > 100 {
		pageSize = 20
	}

	offset := (page - 1) * pageSize

	// Build query
	query := h.db.Model(&models.OnboardingEmail{}).Preload("User")

	if emailType != "" {
		query = query.Where("email_type = ?", emailType)
	}
	if userID > 0 {
		query = query.Where("user_id = ?", userID)
	}
	if opened == "true" {
		query = query.Where("opened_at IS NOT NULL")
	} else if opened == "false" {
		query = query.Where("opened_at IS NULL")
	}

	// Count total
	var total int64
	if err := query.Count(&total).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to count emails"})
	}

	// Get paginated results
	var emails []models.OnboardingEmail
	if err := query.Order("sent_at DESC").Offset(offset).Limit(pageSize).Find(&emails).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch emails"})
	}

	// Transform to response
	data := make([]OnboardingEmailResponse, len(emails))
	for i, email := range emails {
		data[i] = transformOnboardingEmail(email)
	}

	totalPages := int(total) / pageSize
	if int(total)%pageSize > 0 {
		totalPages++
	}

	return c.JSON(http.StatusOK, OnboardingEmailListResponse{
		Data:       data,
		Total:      total,
		Page:       page,
		PageSize:   pageSize,
		TotalPages: totalPages,
	})
}

// GetStats returns onboarding email statistics
// GET /api/admin/onboarding-emails/stats
func (h *AdminOnboardingHandler) GetStats(c echo.Context) error {
	var stats OnboardingStatsResponse

	// No residence email stats
	h.db.Model(&models.OnboardingEmail{}).
		Where("email_type = ?", models.OnboardingEmailNoResidence).
		Count(&stats.NoResidenceTotal)
	h.db.Model(&models.OnboardingEmail{}).
		Where("email_type = ? AND opened_at IS NOT NULL", models.OnboardingEmailNoResidence).
		Count(&stats.NoResidenceOpened)

	// No tasks email stats
	h.db.Model(&models.OnboardingEmail{}).
		Where("email_type = ?", models.OnboardingEmailNoTasks).
		Count(&stats.NoTasksTotal)
	h.db.Model(&models.OnboardingEmail{}).
		Where("email_type = ? AND opened_at IS NOT NULL", models.OnboardingEmailNoTasks).
		Count(&stats.NoTasksOpened)

	// Calculate rates
	if stats.NoResidenceTotal > 0 {
		stats.NoResidenceRate = float64(stats.NoResidenceOpened) / float64(stats.NoResidenceTotal) * 100
	}
	if stats.NoTasksTotal > 0 {
		stats.NoTasksRate = float64(stats.NoTasksOpened) / float64(stats.NoTasksTotal) * 100
	}

	stats.TotalSent = stats.NoResidenceTotal + stats.NoTasksTotal
	stats.TotalOpened = stats.NoResidenceOpened + stats.NoTasksOpened
	if stats.TotalSent > 0 {
		stats.OverallRate = float64(stats.TotalOpened) / float64(stats.TotalSent) * 100
	}

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

// GetByUser returns onboarding emails for a specific user
// GET /api/admin/onboarding-emails/user/:user_id
func (h *AdminOnboardingHandler) GetByUser(c echo.Context) error {
	userID, err := strconv.ParseUint(c.Param("user_id"), 10, 32)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid user ID"})
	}

	var emails []models.OnboardingEmail
	if err := h.db.Where("user_id = ?", userID).Order("sent_at DESC").Find(&emails).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch emails"})
	}

	// Transform to response
	data := make([]OnboardingEmailResponse, len(emails))
	for i, email := range emails {
		data[i] = transformOnboardingEmail(email)
	}

	return c.JSON(http.StatusOK, map[string]interface{}{
		"data":    data,
		"user_id": userID,
		"count":   len(data),
	})
}

// Get returns a single onboarding email by ID
// GET /api/admin/onboarding-emails/:id
func (h *AdminOnboardingHandler) Get(c echo.Context) error {
	id, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid ID"})
	}

	var email models.OnboardingEmail
	if err := h.db.Preload("User").First(&email, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Email not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch email"})
	}

	return c.JSON(http.StatusOK, transformOnboardingEmail(email))
}

// Delete removes an onboarding email record
// DELETE /api/admin/onboarding-emails/:id
func (h *AdminOnboardingHandler) Delete(c echo.Context) error {
	id, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid ID"})
	}

	result := h.db.Delete(&models.OnboardingEmail{}, id)
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete email"})
	}
	if result.RowsAffected == 0 {
		return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Email not found"})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{"message": "Email record deleted"})
}

// BulkDelete removes multiple onboarding email records
// DELETE /api/admin/onboarding-emails/bulk
func (h *AdminOnboardingHandler) BulkDelete(c echo.Context) error {
	var req struct {
		IDs []uint `json:"ids" validate:"required"`
	}
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request"})
	}

	if len(req.IDs) == 0 {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "No IDs provided"})
	}

	result := h.db.Delete(&models.OnboardingEmail{}, req.IDs)
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete emails"})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{
		"message": "Emails deleted",
		"count":   result.RowsAffected,
	})
}

// SendOnboardingEmailRequest represents a request to send an onboarding email
type SendOnboardingEmailRequest struct {
	UserID    uint   `json:"user_id" validate:"required"`
	EmailType string `json:"email_type" validate:"required"`
}

// Send sends an onboarding email to a specific user
// POST /api/admin/onboarding-emails/send
func (h *AdminOnboardingHandler) Send(c echo.Context) error {
	if h.onboardingService == nil {
		return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{"error": "Onboarding email service not configured"})
	}

	var req SendOnboardingEmailRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request: user_id and email_type are required"})
	}
	if err := c.Validate(&req); err != nil {
		return err
	}

	// Validate email type
	var emailType models.OnboardingEmailType
	switch req.EmailType {
	case "no_residence":
		emailType = models.OnboardingEmailNoResidence
	case "no_tasks":
		emailType = models.OnboardingEmailNoTasks
	default:
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid email_type. Must be 'no_residence' or 'no_tasks'"})
	}

	// Get user email for response
	var user models.User
	if err := h.db.First(&user, req.UserID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "User not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch user"})
	}

	// Send the email
	if err := h.onboardingService.SendOnboardingEmailToUser(req.UserID, emailType); err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to send onboarding email"})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{
		"message":    "Onboarding email sent successfully",
		"user_id":    req.UserID,
		"email":      user.Email,
		"email_type": req.EmailType,
	})
}

// transformOnboardingEmail converts a model to response format
func transformOnboardingEmail(email models.OnboardingEmail) OnboardingEmailResponse {
	resp := OnboardingEmailResponse{
		ID:         email.ID,
		UserID:     email.UserID,
		EmailType:  string(email.EmailType),
		SentAt:     email.SentAt.Format("2006-01-02T15:04:05Z"),
		TrackingID: email.TrackingID,
		CreatedAt:  email.CreatedAt.Format("2006-01-02T15:04:05Z"),
	}

	if email.OpenedAt != nil {
		opened := email.OpenedAt.Format("2006-01-02T15:04:05Z")
		resp.OpenedAt = &opened
	}

	// Include user info if loaded
	if email.User.ID != 0 {
		resp.UserEmail = email.User.Email
		if email.User.FirstName != "" || email.User.LastName != "" {
			resp.UserName = email.User.FirstName + " " + email.User.LastName
		} else {
			resp.UserName = email.User.Username
		}
	}

	return resp
}
