package handler

import (
	"context"
	"eslogad-be/internal/appcontext"
	"net/http"
	"strconv"

	"eslogad-be/internal/contract"

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

type LetterOutgoingService interface {
	CreateOutgoingLetter(ctx context.Context, req *contract.CreateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error)
	GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*contract.OutgoingLetterResponse, error)
	ListOutgoingLetters(ctx context.Context, req *contract.ListOutgoingLettersRequest) (*contract.ListOutgoingLettersResponse, error)
	UpdateOutgoingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error)
	DeleteOutgoingLetter(ctx context.Context, id uuid.UUID) error

	SubmitForApproval(ctx context.Context, letterID uuid.UUID) error
	ApproveOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.ApproveLetterRequest) error
	RejectOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.RejectLetterRequest) error
	SendOutgoingLetter(ctx context.Context, letterID uuid.UUID) error
	ArchiveOutgoingLetter(ctx context.Context, letterID uuid.UUID) error

	AddRecipients(ctx context.Context, letterID uuid.UUID, req *contract.AddRecipientsRequest) error
	UpdateRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID, req *contract.UpdateRecipientRequest) error
	RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID) error

	AddAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error
	RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error

	CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateDiscussionRequest) (*contract.DiscussionResponse, error)
	UpdateDiscussion(ctx context.Context, discussionID uuid.UUID, req *contract.UpdateDiscussionRequest) error
	DeleteDiscussion(ctx context.Context, discussionID uuid.UUID) error
}

type LetterOutgoingHandler struct {
	svc LetterOutgoingService
}

func NewLetterOutgoingHandler(svc LetterOutgoingService) *LetterOutgoingHandler {
	return &LetterOutgoingHandler{svc: svc}
}

func (h *LetterOutgoingHandler) CreateOutgoingLetter(c *gin.Context) {
	var req contract.CreateOutgoingLetterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
		return
	}
	ctx := c.Request.Context()
	req.UserID = appcontext.FromGinContext(ctx).UserID

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

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

func (h *LetterOutgoingHandler) GetOutgoingLetter(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) ListOutgoingLetters(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
	offset := (page - 1) * limit

	status := c.Query("status")
	query := c.Query("q")
	createdByStr := c.Query("created_by")
	receiverInstitutionStr := c.Query("receiver_institution_id")

	var statusPtr *string
	var queryPtr *string
	var createdByPtr *uuid.UUID
	var receiverInstitutionPtr *uuid.UUID

	if status != "" {
		statusPtr = &status
	}
	if query != "" {
		queryPtr = &query
	}
	if createdByStr != "" {
		if createdBy, err := uuid.Parse(createdByStr); err == nil {
			createdByPtr = &createdBy
		}
	}
	if receiverInstitutionStr != "" {
		if receiverInstitution, err := uuid.Parse(receiverInstitutionStr); err == nil {
			receiverInstitutionPtr = &receiverInstitution
		}
	}

	req := &contract.ListOutgoingLettersRequest{
		Limit:                 limit,
		Offset:                offset,
		Status:                statusPtr,
		Query:                 queryPtr,
		CreatedBy:             createdByPtr,
		ReceiverInstitutionID: receiverInstitutionPtr,
	}

	resp, err := h.svc.ListOutgoingLetters(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))
}

func (h *LetterOutgoingHandler) UpdateOutgoingLetter(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

	var req contract.UpdateOutgoingLetterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) DeleteOutgoingLetter(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) SubmitForApproval(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

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

	c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "submitted for approval"})
}

func (h *LetterOutgoingHandler) ApproveOutgoingLetter(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

	var req contract.ApproveLetterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) RejectOutgoingLetter(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

	var req contract.RejectLetterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) SendOutgoingLetter(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) ArchiveOutgoingLetter(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) AddRecipients(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

	var req contract.AddRecipientsRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) UpdateRecipient(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
		return
	}

	recipientID, err := uuid.Parse(c.Param("recipient_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid recipient id", Code: http.StatusBadRequest})
		return
	}

	var req contract.UpdateRecipientRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) RemoveRecipient(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
		return
	}

	recipientID, err := uuid.Parse(c.Param("recipient_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid recipient id", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) AddAttachments(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
		return
	}

	var req contract.AddAttachmentsRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) RemoveAttachment(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
		return
	}

	attachmentID, err := uuid.Parse(c.Param("attachment_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid attachment id", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) CreateDiscussion(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
		return
	}

	var req contract.CreateDiscussionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) UpdateDiscussion(c *gin.Context) {
	discussionID, err := uuid.Parse(c.Param("discussion_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid discussion id", Code: http.StatusBadRequest})
		return
	}

	var req contract.UpdateDiscussionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
		return
	}

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

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

func (h *LetterOutgoingHandler) DeleteDiscussion(c *gin.Context) {
	discussionID, err := uuid.Parse(c.Param("discussion_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid discussion id", Code: http.StatusBadRequest})
		return
	}

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

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