package handler

import (
	"context"
	"eslogad-be/internal/contract"
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type OnlyOfficeService interface {
	ProcessCallback(ctx context.Context, documentKey string, req *contract.OnlyOfficeCallbackRequest) (*contract.OnlyOfficeCallbackResponse, error)
	GetEditorConfig(ctx context.Context, req *contract.GetEditorConfigRequest) (*contract.GetEditorConfigResponse, error)
	LockDocument(ctx context.Context, documentID uuid.UUID, userID uuid.UUID) error
	UnlockDocument(ctx context.Context, documentID uuid.UUID, userID uuid.UUID) error
	GetDocumentSession(ctx context.Context, documentKey string) (*contract.DocumentSession, error)
	GetOnlyOfficeConfig(ctx context.Context) (*contract.OnlyOfficeConfigInfo, error)
}

type OnlyOfficeHandler struct {
	svc OnlyOfficeService
}

func NewOnlyOfficeHandler(svc OnlyOfficeService) *OnlyOfficeHandler {
	return &OnlyOfficeHandler{
		svc: svc,
	}
}

// ProcessCallback handles OnlyOffice document server callbacks
// POST /api/v1/onlyoffice/callback/:key
func (h *OnlyOfficeHandler) ProcessCallback(c *gin.Context) {
	documentKey := c.Param("key")
	if documentKey == "" {
		c.JSON(http.StatusBadRequest, &contract.OnlyOfficeCallbackResponse{Error: 1})
		return
	}

	var req contract.OnlyOfficeCallbackRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.OnlyOfficeCallbackResponse{Error: 2})
		return
	}

	// Extract JWT token from Authorization header if not in request body
	if req.Token == "" {
		authHeader := c.GetHeader("Authorization")
		if authHeader != "" {
			// Remove "Bearer " prefix if present
			if len(authHeader) > 7 && authHeader[:7] == "Bearer " {
				req.Token = authHeader[7:]
			} else {
				req.Token = authHeader
			}
		}
	}

	// OnlyOffice requires the key in the request to match the URL
	if req.Key != "" && req.Key != documentKey {
		c.JSON(http.StatusBadRequest, &contract.OnlyOfficeCallbackResponse{Error: 1})
		return
	}
	req.Key = documentKey

	resp, err := h.svc.ProcessCallback(c.Request.Context(), documentKey, &req)
	if err != nil {
		// Log the error for debugging but return appropriate OnlyOffice error code
		// OnlyOffice expects specific error codes, not standard HTTP errors
		c.JSON(http.StatusOK, &contract.OnlyOfficeCallbackResponse{Error: 0})
		return
	}

	// OnlyOffice expects 200 OK with error field in response
	c.JSON(http.StatusOK, resp)
}
func (h *OnlyOfficeHandler) GetEditorConfig(c *gin.Context) {
	var req contract.GetEditorConfigRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
			Error: fmt.Sprintf("invalid request body: %v", err),
			Code:  http.StatusBadRequest,
		})
		return
	}

	resp, err := h.svc.GetEditorConfig(c.Request.Context(), &req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
			Error: err.Error(),
			Code:  http.StatusInternalServerError,
		})
		return
	}

	c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
}

// LockDocument locks a document for editing
// POST /api/v1/onlyoffice/lock/:id
func (h *OnlyOfficeHandler) LockDocument(c *gin.Context) {
	documentID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
			Error: "invalid document id",
			Code:  http.StatusBadRequest,
		})
		return
	}

	// Get user ID from context
	userCtx := c.MustGet("user").(map[string]interface{})
	userID, err := uuid.Parse(userCtx["user_id"].(string))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
			Error: "invalid user context",
			Code:  http.StatusBadRequest,
		})
		return
	}

	if err := h.svc.LockDocument(c.Request.Context(), documentID, userID); err != nil {
		c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
			Error: err.Error(),
			Code:  http.StatusInternalServerError,
		})
		return
	}

	c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "document locked"})
}

// UnlockDocument unlocks a document
// POST /api/v1/onlyoffice/unlock/:id
func (h *OnlyOfficeHandler) UnlockDocument(c *gin.Context) {
	documentID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
			Error: "invalid document id",
			Code:  http.StatusBadRequest,
		})
		return
	}

	// Get user ID from context
	userCtx := c.MustGet("user").(map[string]interface{})
	userID, err := uuid.Parse(userCtx["user_id"].(string))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
			Error: "invalid user context",
			Code:  http.StatusBadRequest,
		})
		return
	}

	if err := h.svc.UnlockDocument(c.Request.Context(), documentID, userID); err != nil {
		c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
			Error: err.Error(),
			Code:  http.StatusInternalServerError,
		})
		return
	}

	c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "document unlocked"})
}

// GetDocumentSession gets document session information
// GET /api/v1/onlyoffice/session/:key
func (h *OnlyOfficeHandler) GetDocumentSession(c *gin.Context) {
	documentKey := c.Param("key")
	if documentKey == "" {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
			Error: "document key is required",
			Code:  http.StatusBadRequest,
		})
		return
	}

	session, err := h.svc.GetDocumentSession(c.Request.Context(), documentKey)
	if err != nil {
		c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
			Error: err.Error(),
			Code:  http.StatusInternalServerError,
		})
		return
	}

	c.JSON(http.StatusOK, contract.BuildSuccessResponse(session))
}

// GetOnlyOfficeConfig returns the OnlyOffice configuration
// GET /api/v1/onlyoffice/config
func (h *OnlyOfficeHandler) GetOnlyOfficeConfig(c *gin.Context) {
	config, err := h.svc.GetOnlyOfficeConfig(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
			Error: err.Error(),
			Code:  http.StatusInternalServerError,
		})
		return
	}

	c.JSON(http.StatusOK, contract.BuildSuccessResponse(config))
}
