package handlers

import (
	"net/http"
	"strconv"

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

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

// AdminContractorHandler handles admin contractor management endpoints
type AdminContractorHandler struct {
	db *gorm.DB
}

// NewAdminContractorHandler creates a new admin contractor handler
func NewAdminContractorHandler(db *gorm.DB) *AdminContractorHandler {
	return &AdminContractorHandler{db: db}
}

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

	var contractors []models.Contractor
	var total int64

	query := h.db.Model(&models.Contractor{}).
		Preload("Residence").
		Preload("CreatedBy").
		Preload("Specialties")

	// Apply search
	if filters.Search != "" {
		search := "%" + filters.Search + "%"
		query = query.Where(
			"name ILIKE ? OR company ILIKE ? OR email ILIKE ? OR phone ILIKE ?",
			search, search, search, search,
		)
	}

	// Apply filters
	if filters.IsActive != nil {
		query = query.Where("is_active = ?", *filters.IsActive)
	}
	if filters.IsFavorite != nil {
		query = query.Where("is_favorite = ?", *filters.IsFavorite)
	}
	if filters.ResidenceID != nil {
		query = query.Where("residence_id = ?", *filters.ResidenceID)
	}

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

	// Apply sorting (allowlist prevents SQL injection via sort_by parameter)
	sortBy := filters.GetSafeSortBy([]string{
		"id", "name", "company", "email", "phone", "city",
		"created_at", "updated_at", "is_active", "is_favorite", "rating",
	}, "created_at")
	query = query.Order(sortBy + " " + filters.GetSortDir())

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

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

	// Build response
	responses := make([]dto.ContractorResponse, len(contractors))
	for i, contractor := range contractors {
		responses[i] = h.toContractorResponse(&contractor)
	}

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

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

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

	response := dto.ContractorDetailResponse{
		ContractorResponse: h.toContractorResponse(&contractor),
	}

	// Get task count
	var taskCount int64
	h.db.Model(&models.Task{}).Where("contractor_id = ?", contractor.ID).Count(&taskCount)
	response.TaskCount = int(taskCount)

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

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

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

	var req dto.UpdateContractorRequest
	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"})
		}
		contractor.ResidenceID = req.ResidenceID
	}
	// 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"})
		}
		contractor.CreatedByID = *req.CreatedByID
	}
	if req.Name != nil {
		contractor.Name = *req.Name
	}
	if req.Company != nil {
		contractor.Company = *req.Company
	}
	if req.Phone != nil {
		contractor.Phone = *req.Phone
	}
	if req.Email != nil {
		contractor.Email = *req.Email
	}
	if req.Website != nil {
		contractor.Website = *req.Website
	}
	if req.Notes != nil {
		contractor.Notes = *req.Notes
	}
	if req.StreetAddress != nil {
		contractor.StreetAddress = *req.StreetAddress
	}
	if req.City != nil {
		contractor.City = *req.City
	}
	if req.StateProvince != nil {
		contractor.StateProvince = *req.StateProvince
	}
	if req.PostalCode != nil {
		contractor.PostalCode = *req.PostalCode
	}
	if req.Rating != nil {
		contractor.Rating = req.Rating
	}
	if req.IsFavorite != nil {
		contractor.IsFavorite = *req.IsFavorite
	}
	if req.IsActive != nil {
		contractor.IsActive = *req.IsActive
	}

	// Update specialties if provided
	if req.SpecialtyIDs != nil {
		// Clear existing specialties
		h.db.Model(&contractor).Association("Specialties").Clear()
		// Add new specialties
		if len(req.SpecialtyIDs) > 0 {
			var specialties []models.ContractorSpecialty
			h.db.Where("id IN ?", req.SpecialtyIDs).Find(&specialties)
			h.db.Model(&contractor).Association("Specialties").Append(specialties)
		}
	}

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

	h.db.Preload("Residence").Preload("CreatedBy").Preload("Specialties").First(&contractor, id)
	return c.JSON(http.StatusOK, h.toContractorResponse(&contractor))
}

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

	// Verify residence exists if provided
	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 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"})
	}

	contractor := models.Contractor{
		ResidenceID:   req.ResidenceID,
		CreatedByID:   req.CreatedByID,
		Name:          req.Name,
		Company:       req.Company,
		Phone:         req.Phone,
		Email:         req.Email,
		Website:       req.Website,
		Notes:         req.Notes,
		StreetAddress: req.StreetAddress,
		City:          req.City,
		StateProvince: req.StateProvince,
		PostalCode:    req.PostalCode,
		IsFavorite:    req.IsFavorite,
		IsActive:      true,
	}

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

	// Add specialties if provided
	if len(req.SpecialtyIDs) > 0 {
		var specialties []models.ContractorSpecialty
		h.db.Where("id IN ?", req.SpecialtyIDs).Find(&specialties)
		h.db.Model(&contractor).Association("Specialties").Append(specialties)
	}

	h.db.Preload("Residence").Preload("CreatedBy").Preload("Specialties").First(&contractor, contractor.ID)
	return c.JSON(http.StatusCreated, h.toContractorResponse(&contractor))
}

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

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

	// Soft delete
	contractor.IsActive = false
	if err := h.db.Save(&contractor).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete contractor"})
	}

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

// BulkDelete handles DELETE /api/admin/contractors/bulk
func (h *AdminContractorHandler) 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 - deactivate all
	if err := h.db.Model(&models.Contractor{}).Where("id IN ?", req.IDs).Update("is_active", false).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete contractors"})
	}

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

func (h *AdminContractorHandler) toContractorResponse(contractor *models.Contractor) dto.ContractorResponse {
	response := dto.ContractorResponse{
		ID:            contractor.ID,
		ResidenceID:   contractor.ResidenceID,
		CreatedByID:   contractor.CreatedByID,
		Name:          contractor.Name,
		Company:       contractor.Company,
		Phone:         contractor.Phone,
		Email:         contractor.Email,
		Website:       contractor.Website,
		Notes:         contractor.Notes,
		StreetAddress: contractor.StreetAddress,
		City:          contractor.City,
		StateProvince: contractor.StateProvince,
		PostalCode:    contractor.PostalCode,
		IsFavorite:    contractor.IsFavorite,
		IsActive:      contractor.IsActive,
		CreatedAt:     contractor.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:     contractor.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	}

	if contractor.Residence != nil && contractor.Residence.ID != 0 {
		response.ResidenceName = contractor.Residence.Name
	}
	if contractor.CreatedBy.ID != 0 {
		response.CreatedByName = contractor.CreatedBy.Username
	}
	if contractor.Rating != nil {
		response.Rating = contractor.Rating
	}

	// Add specialties (names and IDs)
	for _, s := range contractor.Specialties {
		response.Specialties = append(response.Specialties, s.Name)
		response.SpecialtyIDs = append(response.SpecialtyIDs, s.ID)
	}

	return response
}
