package handlers

import (
	"fmt"
	"strings"

	"github.com/gofiber/fiber/v2"

	filemanager "wucher/internal/domain/file_manager"
	"wucher/internal/shared/pkg/uuidv7"
	dto "wucher/internal/transport/http/dto"
	requestdto "wucher/internal/transport/http/dto/request"
	responsedto "wucher/internal/transport/http/dto/response"
	shareddto "wucher/internal/transport/http/dto/shared"
	"wucher/internal/transport/http/jsonapi"
	"wucher/internal/transport/http/response"
	"wucher/internal/transport/http/validators"
)

type FileManagerAttachmentHandler struct {
	svc      filemanager.Service
	validate *validators.Validator
}

var (
	_ = dto.ErrorResponse{}
	_ = dto.GenericDeleteResponse{}
)

func NewFileManagerAttachmentHandler(svc filemanager.Service) *FileManagerAttachmentHandler {
	return &FileManagerAttachmentHandler{
		svc:      svc,
		validate: validators.New(),
	}
}

// CreateAttachment godoc
// @Summary Attach file to business reference
// @ID FileManagerCreateAttachment
// @Description Create generic attachment relation between a file and business reference (ref_type/ref_id).
// @Tags File Manager
// @Accept json
// @Produce json
// @Param request body requestdto.FileManagerAttachmentCreateRequest true "JSON:API attachment create request"
// @Success 201 {object} responsedto.FileManagerAttachmentResponse
// @Failure 422 {object} dto.ErrorResponse
// @Failure 400 {object} dto.ErrorResponse
// @Router /api/v1/file-manager/attachments/create [post]
func (h *FileManagerAttachmentHandler) CreateAttachment(c *fiber.Ctx) error {
	var req requestdto.FileManagerAttachmentCreateRequest
	if err := c.BodyParser(&req); err != nil {
		return writeFileManagerError(c, fiber.StatusBadRequest, "Invalid JSON", err.Error(), nil)
	}
	if errs := h.validate.ValidateStruct(req); errs != nil {
		return writeFileManagerError(c, fiber.StatusUnprocessableEntity, "Validation error", "invalid attachment payload", nil)
	}

	fileID, err := uuidv7.ParseString(strings.TrimSpace(req.Data.Attributes.FileID))
	if err != nil {
		return writeFileManagerError(c, fiber.StatusUnprocessableEntity, "Validation error", "file_id is invalid UUID", &jsonapi.ErrorSource{Pointer: "/data/attributes/file_id"})
	}

	sortOrder := 0
	if req.Data.Attributes.SortOrder != nil {
		sortOrder = *req.Data.Attributes.SortOrder
	}
	if sortOrder < 0 {
		return writeFileManagerError(c, fiber.StatusUnprocessableEntity, "Validation error", "sort_order must be greater than or equal to 0", &jsonapi.ErrorSource{Pointer: "/data/attributes/sort_order"})
	}

	isPrimary := false
	if req.Data.Attributes.IsPrimary != nil {
		isPrimary = *req.Data.Attributes.IsPrimary
	}

	created, err := h.svc.CreateAttachment(c.Context(), filemanager.CreateAttachmentParams{
		FileID:    fileID,
		RefType:   req.Data.Attributes.RefType,
		RefID:     req.Data.Attributes.RefID,
		Category:  req.Data.Attributes.Category,
		SortOrder: sortOrder,
		IsPrimary: isPrimary,
		ActorID:   actorUserID(c),
	})
	if err != nil {
		return writeFileManagerActionError(c, "Attach failed", err)
	}

	return response.Write(c, fiber.StatusCreated, fileManagerAttachmentResource(created))
}

// GetAttachment godoc
// @Summary Get attachment detail
// @ID FileManagerGetAttachment
// @Description Get one generic attachment relation by UUID.
// @Tags File Manager
// @Produce json
// @Param uuid path string true "Attachment UUID (UUIDv7)"
// @Success 200 {object} responsedto.FileManagerAttachmentResponse
// @Failure 404 {object} dto.ErrorResponse
// @Failure 422 {object} dto.ErrorResponse
// @Router /api/v1/file-manager/attachments/get/{uuid} [get]
func (h *FileManagerAttachmentHandler) GetAttachment(c *fiber.Ctx) error {
	id, err := h.parseUUIDParam(c, "uuid", "/path/uuid")
	if err != nil {
		return err
	}

	row, err := h.svc.GetAttachmentByID(c.Context(), id)
	if err != nil {
		return writeFileManagerActionError(c, "Get failed", err)
	}
	if row == nil {
		return writeFileManagerError(c, fiber.StatusNotFound, "Not found", "attachment not found", nil)
	}

	return response.Write(c, fiber.StatusOK, fileManagerAttachmentResource(row))
}

// ListAttachments godoc
// @Summary List attachments by business reference
// @ID FileManagerListAttachmentsByReference
// @Description List generic attachment relations by ref_type and ref_id.
// @Tags File Manager
// @Produce json
// @Param ref_type query string true "Business reference type, e.g. helicopter"
// @Param ref_id query string true "Business reference ID"
// @Param category query string false "Optional attachment category"
// @Param page[number] query int false "Page number (default 1)"
// @Param page[size] query int false "Page size (default 20, max 100)"
// @Param sort query string false "Sort values: sort_order,-sort_order,created_at,-created_at,updated_at,-updated_at"
// @Success 200 {object} responsedto.FileManagerAttachmentListResponse
// @Failure 422 {object} dto.ErrorResponse
// @Failure 400 {object} dto.ErrorResponse
// @Router /api/v1/file-manager/attachments/get-all [get]
func (h *FileManagerAttachmentHandler) ListAttachments(c *fiber.Ctx) error {
	refType := strings.TrimSpace(c.Query("ref_type"))
	refID := strings.TrimSpace(c.Query("ref_id"))
	if refType == "" || refID == "" {
		return writeFileManagerError(c, fiber.StatusUnprocessableEntity, "Validation error", "ref_type and ref_id are required", &jsonapi.ErrorSource{Pointer: "/query/ref_type"})
	}

	var category *string
	rawCategory := strings.TrimSpace(c.Query("category"))
	if rawCategory != "" {
		category = &rawCategory
	}

	_, _, _ = parseFileManagerAttachmentPagination(c)
	rows, total, err := h.svc.ListAttachmentsByReference(c.Context(), filemanager.ListAttachmentsParams{
		RefType:  refType,
		RefID:    refID,
		Category: category,
		Sort:     c.Query("sort"),
		Limit:    0,
		Offset:   0,
	})
	if err != nil {
		return writeFileManagerActionError(c, "List failed", err)
	}

	data := make([]responsedto.FileManagerAttachmentResource, 0, len(rows))
	for i := range rows {
		data = append(data, fileManagerAttachmentResource(&rows[i]))
	}

	meta := map[string]any{
		"total": total,
	}
	return response.WriteWithMeta(c, fiber.StatusOK, data, meta)
}

// DeleteAttachment godoc
// @Summary Detach attachment relation
// @ID FileManagerDeleteAttachment
// @Description Remove generic attachment relation by UUID.
// @Tags File Manager
// @Produce json
// @Param uuid path string true "Attachment UUID (UUIDv7)"
// @Success 200 {object} dto.GenericDeleteResponse
// @Failure 404 {object} dto.ErrorResponse
// @Failure 422 {object} dto.ErrorResponse
// @Router /api/v1/file-manager/attachments/delete/{uuid} [delete]
func (h *FileManagerAttachmentHandler) DeleteAttachment(c *fiber.Ctx) error {
	id, err := h.parseUUIDParam(c, "uuid", "/path/uuid")
	if err != nil {
		return err
	}

	if err := h.svc.DeleteAttachment(c.Context(), id); err != nil {
		return writeFileManagerActionError(c, "Detach failed", err)
	}

	return response.Write(c, fiber.StatusOK, jsonapi.Resource{
		Type: "file_manager_attachment_delete",
		Attributes: map[string]any{
			"detached": true,
		},
	})
}

func (h *FileManagerAttachmentHandler) parseUUIDParam(c *fiber.Ctx, paramName, sourcePointer string) ([]byte, error) {
	uuidStr := strings.TrimSpace(c.Params(paramName))
	id, err := uuidv7.ParseString(uuidStr)
	if err != nil {
		return nil, writeFileManagerError(c, fiber.StatusUnprocessableEntity, "Validation error", fmt.Sprintf("%s is invalid UUID", paramName), &jsonapi.ErrorSource{Pointer: sourcePointer})
	}
	return id, nil
}

func parseFileManagerAttachmentPagination(c *fiber.Ctx) (pageNumber int, pageSize int, offset int) {
	pageNumber = parseIntDefault(c.Query("page[number]"), 0)
	if pageNumber <= 0 {
		pageNumber = parseIntDefault(c.Query("page"), 1)
	}
	pageSize = parseIntDefault(c.Query("page[size]"), 0)
	if pageSize <= 0 {
		pageSize = parseIntDefault(c.Query("limit"), 0)
	}
	if pageSize <= 0 {
		pageSize = parseIntDefault(c.Query("size"), 20)
	}
	if pageSize > 100 {
		pageSize = 100
	}
	if pageSize < 1 {
		pageSize = 20
	}
	if pageNumber < 1 {
		pageNumber = 1
	}
	offset = (pageNumber - 1) * pageSize
	return
}

func fileManagerAttachmentResource(row *filemanager.Attachment) responsedto.FileManagerAttachmentResource {
	if row == nil {
		return responsedto.FileManagerAttachmentResource{
			Type: "file_manager_attachment",
		}
	}

	category := ""
	if row.Category != nil {
		category = strings.TrimSpace(*row.Category)
	}

	attributes := shareddto.FileManagerAttachmentAttributes{
		FileID:     idString(row.FileID),
		RefType:    strings.TrimSpace(row.RefType),
		RefID:      strings.TrimSpace(row.RefID),
		Category:   category,
		SortOrder:  row.SortOrder,
		IsPrimary:  row.IsPrimary,
		AttachedBy: uuidStringOrEmpty(row.AttachedBy),
		CreatedAt:  timeStringOrEmpty(&row.CreatedAt),
		UpdatedAt:  timeStringOrEmpty(&row.UpdatedAt),
	}

	return responsedto.FileManagerAttachmentResource{
		Type:       "file_manager_attachment",
		ID:         idString(row.ID),
		Attributes: attributes,
	}
}
