package httpapi

import (
	"encoding/json"
	"errors"
	"net/http"

	"github.com/go-chi/chi/v5"

	"commilitia.net/cdrop/internal/jwtauth"
	"commilitia.net/cdrop/internal/transfer"
)

type initReq struct {
	ReceiverName string `json:"receiver_name"`
	FileName     string `json:"file_name"`
	FileSize     int64  `json:"file_size"`
	FileSHA256   string `json:"file_sha256,omitempty"`
}

type initResp struct {
	SessionID string `json:"session_id"`
}

func (s *Server) handleTransferInit(w http.ResponseWriter, r *http.Request) {
	claims, _ := jwtauth.ClaimsFromContext(r.Context())
	sender, _ := jwtauth.DeviceNameFromContext(r.Context())

	var req initReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid json"})
		return
	}

	id, err := s.transfers.Init(r.Context(), transfer.InitParams{
		UserID:       claims.UserID,
		SenderName:   sender,
		ReceiverName: req.ReceiverName,
		FileName:     req.FileName,
		FileSize:     req.FileSize,
		FileSHA256:   req.FileSHA256,
	})
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, initResp{SessionID: id})
}

// handleTransferTransition is the shared body for accept / cancel / fallback /
// done / fail. The target state and any side-fields are bound by the route.
type transitionReq struct {
	Reason           string `json:"reason,omitempty"`
	BytesTransferred int64  `json:"bytes_transferred,omitempty"`
}

func (s *Server) transitionHandler(target, mode string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		claims, _ := jwtauth.ClaimsFromContext(r.Context())
		id := chi.URLParam(r, "id")
		if id == "" {
			writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing id"})
			return
		}

		var req transitionReq
		if r.ContentLength > 0 {
			if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
				writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid json"})
				return
			}
		}

		err := s.transfers.Transition(
			r.Context(), claims.UserID, id, target,
			mode, req.Reason, req.BytesTransferred,
		)
		switch {
		case err == nil:
			w.WriteHeader(http.StatusNoContent)
		case errors.Is(err, transfer.ErrNotFound):
			writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"})
		case errors.Is(err, transfer.ErrForbidden):
			writeJSON(w, http.StatusForbidden, map[string]string{"error": "forbidden"})
		case errors.Is(err, transfer.ErrInvalidTransition):
			writeJSON(w, http.StatusConflict, map[string]string{"error": err.Error()})
		case errors.Is(err, transfer.ErrRelayUnavailable):
			writeJSON(w, http.StatusServiceUnavailable,
				map[string]string{"error": "relay unavailable, retry later"})
		default:
			writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		}
	}
}
