package handlers

import (
	"net/http"
	"strconv"
	"time"

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

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

// AdminTaskHandler handles admin task management endpoints
type AdminTaskHandler struct {
	db *gorm.DB
}

// NewAdminTaskHandler creates a new admin task handler
func NewAdminTaskHandler(db *gorm.DB) *AdminTaskHandler {
	return &AdminTaskHandler{db: db}
}

// List handles GET /api/admin/tasks
func (h *AdminTaskHandler) List(c echo.Context) error {
	var filters dto.TaskFilters
	if err := c.Bind(&filters); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	var tasks []models.Task
	var total int64

	query := h.db.Model(&models.Task{}).
		Preload("Residence").
		Preload("CreatedBy").
		Preload("Category").
		Preload("Priority")

	// Apply search
	if filters.Search != "" {
		search := "%" + filters.Search + "%"
		query = query.Where("title ILIKE ? OR description ILIKE ?", search, search)
	}

	// Apply filters
	if filters.ResidenceID != nil {
		query = query.Where("residence_id = ?", *filters.ResidenceID)
	}
	if filters.CategoryID != nil {
		query = query.Where("category_id = ?", *filters.CategoryID)
	}
	if filters.PriorityID != nil {
		query = query.Where("priority_id = ?", *filters.PriorityID)
	}
	if filters.InProgress != nil {
		query = query.Where("in_progress = ?", *filters.InProgress)
	}
	if filters.IsCancelled != nil {
		query = query.Where("is_cancelled = ?", *filters.IsCancelled)
	}
	if filters.IsArchived != nil {
		query = query.Where("is_archived = ?", *filters.IsArchived)
	}

	// Get total count
	query.Count(&total)

	// Apply sorting (allowlist prevents SQL injection via sort_by parameter)
	sortBy := filters.GetSafeSortBy([]string{
		"id", "title", "created_at", "updated_at", "due_date", "next_due_date",
		"residence_id", "category_id", "priority_id", "in_progress",
		"is_cancelled", "is_archived", "estimated_cost", "actual_cost",
	}, "created_at")
	query = query.Order(sortBy + " " + filters.GetSortDir())

	// Apply pagination
	query = query.Offset(filters.GetOffset()).Limit(filters.GetPerPage())

	if err := query.Find(&tasks).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch tasks"})
	}

	// Build response
	responses := make([]dto.TaskResponse, len(tasks))
	for i, task := range tasks {
		responses[i] = h.toTaskResponse(&task)
	}

	return c.JSON(http.StatusOK, dto.NewPaginatedResponse(responses, total, filters.GetPage(), filters.GetPerPage()))
}

// Get handles GET /api/admin/tasks/:id
func (h *AdminTaskHandler) 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 task ID"})
	}

	var task models.Task
	if err := h.db.
		Preload("Residence").
		Preload("CreatedBy").
		Preload("AssignedTo").
		Preload("Category").
		Preload("Priority").
		Preload("Frequency").
		Preload("Completions").
		First(&task, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Task not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch task"})
	}

	response := dto.TaskDetailResponse{
		TaskResponse: h.toTaskResponse(&task),
	}

	if task.AssignedTo != nil {
		response.AssignedTo = &dto.UserSummary{
			ID:       task.AssignedTo.ID,
			Username: task.AssignedTo.Username,
			Email:    task.AssignedTo.Email,
		}
	}

	response.CompletionCount = len(task.Completions)

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

// Update handles PUT /api/admin/tasks/:id
func (h *AdminTaskHandler) Update(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 task ID"})
	}

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

	var req dto.UpdateTaskRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	// Verify residence if changing
	if req.ResidenceID != nil {
		var residence models.Residence
		if err := h.db.First(&residence, *req.ResidenceID).Error; err != nil {
			return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Residence not found"})
		}
	}
	// Verify created_by if changing
	if req.CreatedByID != nil {
		var user models.User
		if err := h.db.First(&user, *req.CreatedByID).Error; err != nil {
			return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Created by user not found"})
		}
	}
	// Verify assigned_to if changing
	if req.AssignedToID != nil {
		var user models.User
		if err := h.db.First(&user, *req.AssignedToID).Error; err != nil {
			return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Assigned to user not found"})
		}
	}

	// Build update map with only the fields that were provided
	updates := make(map[string]interface{})
	if req.ResidenceID != nil {
		updates["residence_id"] = *req.ResidenceID
	}
	if req.CreatedByID != nil {
		updates["created_by_id"] = *req.CreatedByID
	}
	if req.AssignedToID != nil {
		updates["assigned_to_id"] = *req.AssignedToID
	}
	if req.Title != nil {
		updates["title"] = *req.Title
	}
	if req.Description != nil {
		updates["description"] = *req.Description
	}
	if req.CategoryID != nil {
		updates["category_id"] = *req.CategoryID
	}
	if req.PriorityID != nil {
		updates["priority_id"] = *req.PriorityID
	}
	if req.InProgress != nil {
		updates["in_progress"] = *req.InProgress
	}
	if req.FrequencyID != nil {
		updates["frequency_id"] = *req.FrequencyID
	}
	if req.DueDate != nil {
		if dueDate, err := time.Parse("2006-01-02", *req.DueDate); err == nil {
			updates["due_date"] = dueDate
		}
	}
	if req.NextDueDate != nil {
		if *req.NextDueDate == "" {
			updates["next_due_date"] = nil
		} else if nextDueDate, err := time.Parse("2006-01-02", *req.NextDueDate); err == nil {
			updates["next_due_date"] = nextDueDate
		}
	}
	if req.EstimatedCost != nil {
		updates["estimated_cost"] = *req.EstimatedCost
	}
	if req.ActualCost != nil {
		updates["actual_cost"] = *req.ActualCost
	}
	if req.ContractorID != nil {
		updates["contractor_id"] = *req.ContractorID
	}
	if req.ParentTaskID != nil {
		updates["parent_task_id"] = *req.ParentTaskID
	}
	if req.IsCancelled != nil {
		updates["is_cancelled"] = *req.IsCancelled
	}
	if req.IsArchived != nil {
		updates["is_archived"] = *req.IsArchived
	}

	// Use Updates with map to only update specified fields
	if err := h.db.Model(&task).Updates(updates).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to update task"})
	}

	// Reload with preloads for response
	h.db.Preload("Residence").Preload("CreatedBy").Preload("Category").Preload("Priority").First(&task, id)
	return c.JSON(http.StatusOK, h.toTaskResponse(&task))
}

// Create handles POST /api/admin/tasks
func (h *AdminTaskHandler) Create(c echo.Context) error {
	var req dto.CreateTaskRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	// Verify residence exists
	var residence models.Residence
	if err := h.db.First(&residence, req.ResidenceID).Error; err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Residence not found"})
	}

	// Verify created_by user exists
	var creator models.User
	if err := h.db.First(&creator, req.CreatedByID).Error; err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Creator user not found"})
	}

	task := models.Task{
		ResidenceID:   req.ResidenceID,
		CreatedByID:   req.CreatedByID,
		Title:         req.Title,
		Description:   req.Description,
		CategoryID:    req.CategoryID,
		PriorityID:    req.PriorityID,
		InProgress:    req.InProgress,
		FrequencyID:   req.FrequencyID,
		AssignedToID:  req.AssignedToID,
		ContractorID:  req.ContractorID,
		IsCancelled:   false,
		IsArchived:    false,
	}

	if req.DueDate != nil {
		dueDate, err := time.Parse("2006-01-02", *req.DueDate)
		if err == nil {
			task.DueDate = &dueDate
		}
	}
	if req.EstimatedCost != nil {
		task.EstimatedCost = req.EstimatedCost
	}

	if err := h.db.Create(&task).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to create task"})
	}

	h.db.Preload("Residence").Preload("CreatedBy").Preload("Category").Preload("Priority").First(&task, task.ID)
	return c.JSON(http.StatusCreated, h.toTaskResponse(&task))
}

// Delete handles DELETE /api/admin/tasks/:id
func (h *AdminTaskHandler) 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 task ID"})
	}

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

	// Soft delete - archive and cancel
	task.IsArchived = true
	task.IsCancelled = true
	if err := h.db.Omit("Residence", "CreatedBy", "AssignedTo", "Category", "Priority", "Frequency", "ParentTask", "Completions").Save(&task).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete task"})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{"message": "Task archived successfully"})
}

// BulkDelete handles DELETE /api/admin/tasks/bulk
func (h *AdminTaskHandler) BulkDelete(c echo.Context) error {
	var req dto.BulkDeleteRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	// Soft delete - archive and cancel all
	if err := h.db.Model(&models.Task{}).Where("id IN ?", req.IDs).Updates(map[string]interface{}{
		"is_archived":  true,
		"is_cancelled": true,
	}).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete tasks"})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{"message": "Tasks archived successfully", "count": len(req.IDs)})
}

func (h *AdminTaskHandler) toTaskResponse(task *models.Task) dto.TaskResponse {
	response := dto.TaskResponse{
		ID:           task.ID,
		ResidenceID:  task.ResidenceID,
		CreatedByID:  task.CreatedByID,
		Title:        task.Title,
		Description:  task.Description,
		CategoryID:   task.CategoryID,
		PriorityID:   task.PriorityID,
		InProgress:   task.InProgress,
		FrequencyID:  task.FrequencyID,
		ContractorID: task.ContractorID,
		ParentTaskID: task.ParentTaskID,
		IsCancelled:  task.IsCancelled,
		IsArchived:   task.IsArchived,
		CreatedAt:    task.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:    task.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	}

	if task.Residence.ID != 0 {
		response.ResidenceName = task.Residence.Name
	}
	if task.CreatedBy.ID != 0 {
		response.CreatedByName = task.CreatedBy.Username
	}
	if task.AssignedToID != nil {
		response.AssignedToID = task.AssignedToID
		if task.AssignedTo != nil {
			response.AssignedToName = &task.AssignedTo.Username
		}
	}
	if task.Category != nil {
		response.CategoryName = &task.Category.Name
	}
	if task.Priority != nil {
		response.PriorityName = &task.Priority.Name
	}
	if task.Frequency != nil {
		response.FrequencyName = &task.Frequency.Name
	}
	if task.DueDate != nil {
		dueDate := task.DueDate.Format("2006-01-02")
		response.DueDate = &dueDate
	}
	if task.NextDueDate != nil {
		nextDueDate := task.NextDueDate.Format("2006-01-02")
		response.NextDueDate = &nextDueDate
	}
	if task.EstimatedCost != nil {
		cost, _ := task.EstimatedCost.Float64()
		response.EstimatedCost = &cost
	}
	if task.ActualCost != nil {
		cost, _ := task.ActualCost.Float64()
		response.ActualCost = &cost
	}

	return response
}
