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"
)

// AdminDocumentHandler handles admin document management endpoints
type AdminDocumentHandler struct {
	db *gorm.DB
}

// NewAdminDocumentHandler creates a new admin document handler
func NewAdminDocumentHandler(db *gorm.DB) *AdminDocumentHandler {
	return &AdminDocumentHandler{db: db}
}

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

	var documents []models.Document
	var total int64

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

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

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

	// 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", "document_type",
		"residence_id", "is_active", "expiry_date", "vendor",
	}, "created_at")
	query = query.Order(sortBy + " " + filters.GetSortDir())

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

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

	// Build response
	responses := make([]dto.DocumentResponse, len(documents))
	for i, doc := range documents {
		responses[i] = h.toDocumentResponse(&doc)
	}

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

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

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

	response := dto.DocumentDetailResponse{
		DocumentResponse: h.toDocumentResponse(&document),
	}

	if document.Task != nil {
		response.TaskTitle = &document.Task.Title
	}

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

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

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

	var req dto.UpdateDocumentRequest
	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"})
		}
		document.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"})
		}
		document.CreatedByID = *req.CreatedByID
	}
	if req.Title != nil {
		document.Title = *req.Title
	}
	if req.Description != nil {
		document.Description = *req.Description
	}
	if req.DocumentType != nil {
		document.DocumentType = models.DocumentType(*req.DocumentType)
	}
	if req.FileURL != nil {
		document.FileURL = *req.FileURL
	}
	if req.FileName != nil {
		document.FileName = *req.FileName
	}
	if req.FileSize != nil {
		document.FileSize = req.FileSize
	}
	if req.MimeType != nil {
		document.MimeType = *req.MimeType
	}
	if req.PurchaseDate != nil {
		if purchaseDate, err := time.Parse("2006-01-02", *req.PurchaseDate); err == nil {
			document.PurchaseDate = &purchaseDate
		}
	}
	if req.ExpiryDate != nil {
		if expiryDate, err := time.Parse("2006-01-02", *req.ExpiryDate); err == nil {
			document.ExpiryDate = &expiryDate
		}
	}
	if req.PurchasePrice != nil {
		document.PurchasePrice = req.PurchasePrice
	}
	if req.Vendor != nil {
		document.Vendor = *req.Vendor
	}
	if req.SerialNumber != nil {
		document.SerialNumber = *req.SerialNumber
	}
	if req.ModelNumber != nil {
		document.ModelNumber = *req.ModelNumber
	}
	if req.Provider != nil {
		document.Provider = *req.Provider
	}
	if req.ProviderContact != nil {
		document.ProviderContact = *req.ProviderContact
	}
	if req.ClaimPhone != nil {
		document.ClaimPhone = *req.ClaimPhone
	}
	if req.ClaimEmail != nil {
		document.ClaimEmail = *req.ClaimEmail
	}
	if req.ClaimWebsite != nil {
		document.ClaimWebsite = *req.ClaimWebsite
	}
	if req.Notes != nil {
		document.Notes = *req.Notes
	}
	if req.TaskID != nil {
		document.TaskID = req.TaskID
	}
	if req.IsActive != nil {
		document.IsActive = *req.IsActive
	}

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

	h.db.Preload("Residence").Preload("CreatedBy").Preload("Images").First(&document, id)
	return c.JSON(http.StatusOK, h.toDocumentResponse(&document))
}

// Create handles POST /api/admin/documents
func (h *AdminDocumentHandler) Create(c echo.Context) error {
	var req dto.CreateDocumentRequest
	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"})
	}

	documentType := models.DocumentTypeGeneral
	if req.DocumentType != "" {
		documentType = models.DocumentType(req.DocumentType)
	}

	document := models.Document{
		ResidenceID:  req.ResidenceID,
		CreatedByID:  req.CreatedByID,
		Title:        req.Title,
		Description:  req.Description,
		DocumentType: documentType,
		FileURL:      req.FileURL,
		FileName:     req.FileName,
		FileSize:     req.FileSize,
		MimeType:     req.MimeType,
		Vendor:       req.Vendor,
		SerialNumber: req.SerialNumber,
		ModelNumber:  req.ModelNumber,
		TaskID:       req.TaskID,
		IsActive:     true,
	}

	if req.PurchaseDate != nil {
		purchaseDate, err := time.Parse("2006-01-02", *req.PurchaseDate)
		if err == nil {
			document.PurchaseDate = &purchaseDate
		}
	}
	if req.ExpiryDate != nil {
		expiryDate, err := time.Parse("2006-01-02", *req.ExpiryDate)
		if err == nil {
			document.ExpiryDate = &expiryDate
		}
	}
	if req.PurchasePrice != nil {
		document.PurchasePrice = req.PurchasePrice
	}

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

	h.db.Preload("Residence").Preload("CreatedBy").Preload("Images").First(&document, document.ID)
	return c.JSON(http.StatusCreated, h.toDocumentResponse(&document))
}

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

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

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

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

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

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

func (h *AdminDocumentHandler) toDocumentResponse(doc *models.Document) dto.DocumentResponse {
	response := dto.DocumentResponse{
		ID:              doc.ID,
		ResidenceID:     doc.ResidenceID,
		CreatedByID:     doc.CreatedByID,
		Title:           doc.Title,
		Description:     doc.Description,
		DocumentType:    string(doc.DocumentType),
		FileURL:         doc.FileURL,
		FileName:        doc.FileName,
		FileSize:        doc.FileSize,
		MimeType:        doc.MimeType,
		Vendor:          doc.Vendor,
		SerialNumber:    doc.SerialNumber,
		ModelNumber:     doc.ModelNumber,
		Provider:        doc.Provider,
		ProviderContact: doc.ProviderContact,
		ClaimPhone:      doc.ClaimPhone,
		ClaimEmail:      doc.ClaimEmail,
		ClaimWebsite:    doc.ClaimWebsite,
		Notes:           doc.Notes,
		TaskID:          doc.TaskID,
		IsActive:        doc.IsActive,
		Images:          make([]dto.DocumentImageResponse, 0),
		CreatedAt:       doc.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:       doc.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	}

	if doc.Residence.ID != 0 {
		response.ResidenceName = doc.Residence.Name
	}
	if doc.CreatedBy.ID != 0 {
		response.CreatedByName = doc.CreatedBy.Username
	}
	if doc.ExpiryDate != nil {
		expiryDate := doc.ExpiryDate.Format("2006-01-02")
		response.ExpiryDate = &expiryDate
	}
	if doc.PurchaseDate != nil {
		purchaseDate := doc.PurchaseDate.Format("2006-01-02")
		response.PurchaseDate = &purchaseDate
	}
	if doc.PurchasePrice != nil {
		price, _ := doc.PurchasePrice.Float64()
		response.PurchasePrice = &price
	}

	// Convert images
	for _, img := range doc.Images {
		response.Images = append(response.Images, dto.DocumentImageResponse{
			ID:       img.ID,
			ImageURL: img.ImageURL,
			Caption:  img.Caption,
		})
	}

	return response
}
