package handlers

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"net/http"
	"os"

	"cmr-backend/internal/apperr"
	"cmr-backend/internal/httpx"
	"cmr-backend/internal/service"
)

type AdminAssetHandler struct {
	service *service.AdminAssetService
}

func NewAdminAssetHandler(service *service.AdminAssetService) *AdminAssetHandler {
	return &AdminAssetHandler{service: service}
}

func (h *AdminAssetHandler) ListAssets(w http.ResponseWriter, r *http.Request) {
	result, err := h.service.ListManagedAssets(r.Context(), parseAdminLimit(r))
	if err != nil {
		httpx.WriteError(w, err)
		return
	}
	httpx.WriteJSON(w, http.StatusOK, map[string]any{"data": result})
}

func (h *AdminAssetHandler) GetAsset(w http.ResponseWriter, r *http.Request) {
	result, err := h.service.GetManagedAsset(r.Context(), r.PathValue("assetPublicID"))
	if err != nil {
		httpx.WriteError(w, err)
		return
	}
	httpx.WriteJSON(w, http.StatusOK, map[string]any{"data": result})
}

func (h *AdminAssetHandler) RegisterLink(w http.ResponseWriter, r *http.Request) {
	var req service.RegisterLinkAssetInput
	if err := httpx.DecodeJSON(r, &req); err != nil {
		httpx.WriteError(w, apperr.New(http.StatusBadRequest, "invalid_json", "invalid request body: "+err.Error()))
		return
	}
	result, err := h.service.RegisterExternalLink(r.Context(), req)
	if err != nil {
		httpx.WriteError(w, err)
		return
	}
	httpx.WriteJSON(w, http.StatusCreated, map[string]any{"data": result})
}

func (h *AdminAssetHandler) UploadFile(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseMultipartForm(64 << 20); err != nil {
		httpx.WriteError(w, apperr.New(http.StatusBadRequest, "invalid_multipart", "invalid multipart form: "+err.Error()))
		return
	}

	file, header, err := r.FormFile("file")
	if err != nil {
		httpx.WriteError(w, apperr.New(http.StatusBadRequest, "file_required", "multipart file field 'file' is required"))
		return
	}
	defer file.Close()

	tmpFile, err := os.CreateTemp("", "cmr-upload-*"+header.Filename)
	if err != nil {
		httpx.WriteError(w, err)
		return
	}
	tmpPath := tmpFile.Name()
	defer os.Remove(tmpPath)

	hash := sha256.New()
	written, err := io.Copy(io.MultiWriter(tmpFile, hash), file)
	if err != nil {
		tmpFile.Close()
		httpx.WriteError(w, err)
		return
	}
	if err := tmpFile.Close(); err != nil {
		httpx.WriteError(w, err)
		return
	}

	input := service.UploadAssetFileInput{
		AssetType:   r.FormValue("assetType"),
		AssetCode:   r.FormValue("assetCode"),
		Version:     r.FormValue("version"),
		Title:       stringPtrOrNil(r.FormValue("title")),
		ObjectDir:   stringPtrOrNil(r.FormValue("objectDir")),
		FileName:    header.Filename,
		ContentType: header.Header.Get("Content-Type"),
		FileSize:    written,
		Checksum:    hex.EncodeToString(hash.Sum(nil)),
		TempPath:    tmpPath,
		Status:      r.FormValue("status"),
		Metadata:    parseMetadataJSON(r.FormValue("metadataJson")),
	}

	result, err := h.service.UploadAssetFile(r.Context(), input)
	if err != nil {
		httpx.WriteError(w, err)
		return
	}
	httpx.WriteJSON(w, http.StatusCreated, map[string]any{
		"data": result,
		"meta": map[string]any{
			"uploadedBytes":  written,
			"checksumSha256": input.Checksum,
		},
	})
}

func stringPtrOrNil(value string) *string {
	if value == "" {
		return nil
	}
	return &value
}

func parseMetadataJSON(raw string) map[string]any {
	if raw == "" {
		return nil
	}
	var payload map[string]any
	_ = json.Unmarshal([]byte(raw), &payload)
	return payload
}
