// monorepo/cloud/backend/internal/maplefile/interface/http/file/get_presigned_download_url.go
package file

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"strconv"
	"time"

	"go.uber.org/zap"

	"github.com/gocql/gocql"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/interface/http/middleware"
	svc_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/service/file"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
)

type GetPresignedDownloadURLHTTPRequestDTO struct {
	URLDurationStr string `json:"url_duration,omitempty"` // Optional, duration as string of nanoseconds, defaults to 1 hour
}

type GetPresignedDownloadURLHTTPHandler struct {
	config     *config.Configuration
	logger     *zap.Logger
	service    svc_file.GetPresignedDownloadURLService
	middleware middleware.Middleware
}

func NewGetPresignedDownloadURLHTTPHandler(
	config *config.Configuration,
	logger *zap.Logger,
	service svc_file.GetPresignedDownloadURLService,
	middleware middleware.Middleware,
) *GetPresignedDownloadURLHTTPHandler {
	logger = logger.Named("GetPresignedDownloadURLHTTPHandler")
	return &GetPresignedDownloadURLHTTPHandler{
		config:     config,
		logger:     logger,
		service:    service,
		middleware: middleware,
	}
}

func (*GetPresignedDownloadURLHTTPHandler) Pattern() string {
	return "GET /api/v1/file/{id}/download-url"
}

func (h *GetPresignedDownloadURLHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Apply middleware before handling the request
	h.middleware.Attach(h.Execute)(w, req)
}

func (h *GetPresignedDownloadURLHTTPHandler) unmarshalRequest(
	ctx context.Context,
	r *http.Request,
	fileID gocql.UUID,
) (*svc_file.GetPresignedDownloadURLRequestDTO, error) {
	// For GET requests, read from query parameters instead of body
	urlDurationStr := r.URL.Query().Get("url_duration")

	// Set default URL duration if not provided (1 hour in nanoseconds)
	var urlDuration time.Duration
	if urlDurationStr == "" {
		urlDuration = 1 * time.Hour
	} else {
		// Parse the string to int64 (nanoseconds)
		durationNanos, err := strconv.ParseInt(urlDurationStr, 10, 64)
		if err != nil {
			return nil, httperror.NewForSingleField(http.StatusBadRequest, "url_duration", "Invalid duration format")
		}
		urlDuration = time.Duration(durationNanos)
	}

	// Convert to service DTO
	serviceRequest := &svc_file.GetPresignedDownloadURLRequestDTO{
		FileID:      fileID,
		URLDuration: urlDuration,
	}

	return serviceRequest, nil
}

func (h *GetPresignedDownloadURLHTTPHandler) Execute(w http.ResponseWriter, r *http.Request) {
	// Set response content type
	w.Header().Set("Content-Type", "application/json")

	ctx := r.Context()

	// Extract file ID from URL parameters
	fileIDStr := r.PathValue("id")
	if fileIDStr == "" {
		httperror.RespondWithError(w, r, httperror.NewForBadRequestWithSingleField("file_id", "File ID is required"))
		return
	}

	// Convert string ID to ObjectID
	fileID, err := gocql.ParseUUID(fileIDStr)
	if err != nil {
		h.logger.Error("invalid file ID format",
			zap.String("file_id", fileIDStr),
			zap.Error(err))
		httperror.RespondWithError(w, r, httperror.NewForBadRequestWithSingleField("file_id", "Invalid file ID format"))
		return
	}

	req, err := h.unmarshalRequest(ctx, r, fileID)
	if err != nil {
		httperror.RespondWithError(w, r, err)
		return
	}

	resp, err := h.service.Execute(ctx, req)
	if err != nil {
		httperror.RespondWithError(w, r, err)
		return
	}

	// Encode response
	if resp != nil {
		if err := json.NewEncoder(w).Encode(resp); err != nil {
			h.logger.Error("failed to encode response",
				zap.Any("error", err))
			httperror.RespondWithError(w, r, err)
			return
		}
	} else {
		err := errors.New("no result")
		httperror.RespondWithError(w, r, err)
		return
	}
}
