package handler

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

	"eslogad-be/internal/contract"

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

type LetterService interface {
	CreateIncomingLetter(ctx context.Context, req *contract.CreateIncomingLetterRequest) (*contract.IncomingLetterResponse, error)
	GetIncomingLetterByID(ctx context.Context, id uuid.UUID) (*contract.IncomingLetterResponse, error)
	ListIncomingLetters(ctx context.Context, req *contract.ListIncomingLettersRequest) (*contract.ListIncomingLettersResponse, error)
	GetLetterUnreadCounts(ctx context.Context) (*contract.LetterUnreadCountResponse, error)
	MarkIncomingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error)
	MarkOutgoingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error)
	UpdateIncomingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateIncomingLetterRequest) (*contract.IncomingLetterResponse, error)
	SoftDeleteIncomingLetter(ctx context.Context, id uuid.UUID) error
	BulkArchiveIncomingLetters(ctx context.Context, letterIDs []uuid.UUID) (*contract.BulkArchiveLettersResponse, error)

	CreateDispositions(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*contract.ListDispositionsResponse, error)
	GetEnhancedDispositionsByLetter(ctx context.Context, letterID uuid.UUID) (*contract.ListEnhancedDispositionsResponse, error)

	CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error)
	UpdateDiscussion(ctx context.Context, letterID uuid.UUID, discussionID uuid.UUID, req *contract.UpdateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error)

	GetDepartmentDispositionStatus(ctx context.Context, req *contract.GetDepartmentDispositionStatusRequest) (*contract.ListDepartmentDispositionStatusResponse, error)
	UpdateDispositionStatus(ctx context.Context, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error)

	GetLetterCTA(ctx context.Context, letterID uuid.UUID) (*contract.LetterCTAResponse, error)
}

type LetterHandler struct {
	svc LetterService
}

func NewLetterHandler(svc LetterService) *LetterHandler {
	return &LetterHandler{svc: svc}
}

// Helper functions for common patterns
func (h *LetterHandler) parseUUID(c *gin.Context, param string) (uuid.UUID, bool) {
	id, err := uuid.Parse(c.Param(param))
	if err != nil {
		h.respondError(c, http.StatusBadRequest, "invalid "+param)
		return uuid.Nil, false
	}
	return id, true
}

func (h *LetterHandler) bindJSON(c *gin.Context, req interface{}) bool {
	if err := c.ShouldBindJSON(req); err != nil {
		h.respondError(c, http.StatusBadRequest, "invalid request body")
		return false
	}
	return true
}

func (h *LetterHandler) bindQuery(c *gin.Context, req interface{}) bool {
	if err := c.ShouldBindQuery(req); err != nil {
		h.respondError(c, http.StatusBadRequest, "invalid query parameters")
		return false
	}
	return true
}

func (h *LetterHandler) respondError(c *gin.Context, code int, message string) {
	c.JSON(code, &contract.ErrorResponse{
		Error: message,
		Code:  code,
	})
}

func (h *LetterHandler) respondSuccess(c *gin.Context, code int, data interface{}) {
	c.JSON(code, contract.BuildSuccessResponse(data))
}

func (h *LetterHandler) handleServiceError(c *gin.Context, err error) {
	if err != nil {
		h.respondError(c, http.StatusInternalServerError, err.Error())
	}
}

func (h *LetterHandler) CreateIncomingLetter(c *gin.Context) {
	var req contract.CreateIncomingLetterRequest
	if !h.bindJSON(c, &req) {
		return
	}

	resp, err := h.svc.CreateIncomingLetter(c.Request.Context(), &req)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusCreated, resp)
}

func (h *LetterHandler) GetIncomingLetter(c *gin.Context) {
	id, ok := h.parseUUID(c, "id")
	if !ok {
		return
	}

	resp, err := h.svc.GetIncomingLetterByID(c.Request.Context(), id)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) ListIncomingLetters(c *gin.Context) {
	req := h.parseListRequest(c)

	resp, err := h.svc.ListIncomingLetters(c.Request.Context(), req)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) GetLetterUnreadCounts(c *gin.Context) {
	resp, err := h.svc.GetLetterUnreadCounts(c.Request.Context())
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) MarkIncomingLetterAsRead(c *gin.Context) {
	id, ok := h.parseUUID(c, "id")
	if !ok {
		return
	}

	resp, err := h.svc.MarkIncomingLetterAsRead(c.Request.Context(), id)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) MarkOutgoingLetterAsRead(c *gin.Context) {
	id, ok := h.parseUUID(c, "id")
	if !ok {
		return
	}

	resp, err := h.svc.MarkOutgoingLetterAsRead(c.Request.Context(), id)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) parseListRequest(c *gin.Context) *contract.ListIncomingLettersRequest {
	//appCtx := appcontext.FromGinContext(c)
	//departmentID := appCtx.DepartmentID

	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))

	// Ensure valid pagination values
	if page < 1 {
		page = 1
	}
	if limit < 1 {
		limit = 10
	}
	if limit > 100 {
		limit = 100
	}

	req := &contract.ListIncomingLettersRequest{
		Page:  page,
		Limit: limit,
	}

	if status := c.Query("status"); status != "" {
		req.Status = &status
	}

	if query := c.Query("q"); query != "" {
		req.Query = &query
	}

	// Parse is_read filter
	if isReadStr := c.Query("is_read"); isReadStr != "" {
		isRead := isReadStr == "true" || isReadStr == "1"
		req.IsRead = &isRead
	}

	// Parse priority_ids filter
	if priorityIDsStr := c.QueryArray("priority_ids[]"); len(priorityIDsStr) > 0 {
		priorityIDs := make([]uuid.UUID, 0, len(priorityIDsStr))
		for _, idStr := range priorityIDsStr {
			if id, err := uuid.Parse(idStr); err == nil {
				priorityIDs = append(priorityIDs, id)
			}
		}
		req.PriorityIDs = priorityIDs
	} else if priorityIDStr := c.Query("priority_ids"); priorityIDStr != "" {
		// Also support comma-separated format
		idStrs := strings.Split(priorityIDStr, ",")
		priorityIDs := make([]uuid.UUID, 0, len(idStrs))
		for _, idStr := range idStrs {
			if id, err := uuid.Parse(strings.TrimSpace(idStr)); err == nil {
				priorityIDs = append(priorityIDs, id)
			}
		}
		req.PriorityIDs = priorityIDs
	}

	// Parse is_dispositioned filter
	if isDispositionedStr := c.Query("is_dispositioned"); isDispositionedStr != "" {
		isDispositioned := isDispositionedStr == "true" || isDispositionedStr == "1"
		req.IsDispositioned = &isDispositioned
	}

	// Parse is_archived filter
	if isArchivedStr := c.Query("is_archived"); isArchivedStr != "" {
		isArchived := isArchivedStr == "true" || isArchivedStr == "1"
		req.IsArchived = &isArchived
	}

	//req.DepartmentID = &departmentID

	return req
}

func (h *LetterHandler) UpdateIncomingLetter(c *gin.Context) {
	id, ok := h.parseUUID(c, "id")
	if !ok {
		return
	}

	var req contract.UpdateIncomingLetterRequest
	if !h.bindJSON(c, &req) {
		return
	}

	resp, err := h.svc.UpdateIncomingLetter(c.Request.Context(), id, &req)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) DeleteIncomingLetter(c *gin.Context) {
	id, ok := h.parseUUID(c, "id")
	if !ok {
		return
	}

	if err := h.svc.SoftDeleteIncomingLetter(c.Request.Context(), id); err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, &contract.SuccessResponse{
		Message: "Letter deleted successfully",
	})
}

func (h *LetterHandler) CreateDispositions(c *gin.Context) {
	var req contract.CreateLetterDispositionRequest
	if !h.bindJSON(c, &req) {
		return
	}

	appCtx := appcontext.FromGinContext(c.Request.Context())
	req.FromDepartment = appCtx.DepartmentID

	resp, err := h.svc.CreateDispositions(c.Request.Context(), &req)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusCreated, resp)
}

func (h *LetterHandler) GetEnhancedDispositionsByLetter(c *gin.Context) {
	letterID, ok := h.parseUUID(c, "letter_id")
	if !ok {
		return
	}

	resp, err := h.svc.GetEnhancedDispositionsByLetter(c.Request.Context(), letterID)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) CreateDiscussion(c *gin.Context) {
	letterID, ok := h.parseUUID(c, "letter_id")
	if !ok {
		return
	}

	var req contract.CreateLetterDiscussionRequest
	if !h.bindJSON(c, &req) {
		return
	}

	resp, err := h.svc.CreateDiscussion(c.Request.Context(), letterID, &req)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusCreated, resp)
}

func (h *LetterHandler) UpdateDiscussion(c *gin.Context) {
	letterID, ok := h.parseUUID(c, "letter_id")
	if !ok {
		return
	}

	discussionID, ok := h.parseUUID(c, "discussion_id")
	if !ok {
		return
	}

	var req contract.UpdateLetterDiscussionRequest
	if !h.bindJSON(c, &req) {
		return
	}

	resp, err := h.svc.UpdateDiscussion(c.Request.Context(), letterID, discussionID, &req)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) GetDepartmentDispositionStatus(c *gin.Context) {
	letterID, ok := h.parseUUID(c, "letter_id")
	if !ok {
		return
	}

	departmentID := appcontext.FromGinContext(c.Request.Context()).DepartmentID

	req := &contract.GetDepartmentDispositionStatusRequest{
		LetterIncomingID: letterID,
		DepartmentID:     departmentID,
	}

	resp, err := h.svc.GetDepartmentDispositionStatus(c.Request.Context(), req)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) UpdateDispositionStatus(c *gin.Context) {
	letterID, ok := h.parseUUID(c, "letter_id")
	if !ok {
		return
	}

	var req contract.UpdateDispositionStatusRequest
	if !h.bindJSON(c, &req) {
		return
	}

	req.LetterIncomingID = letterID

	resp, err := h.svc.UpdateDispositionStatus(c.Request.Context(), &req)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) GetLetterCTA(c *gin.Context) {
	letterID, ok := h.parseUUID(c, "letter_id")
	if !ok {
		return
	}

	resp, err := h.svc.GetLetterCTA(c.Request.Context(), letterID)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}

func (h *LetterHandler) BulkArchiveIncomingLetters(c *gin.Context) {
	var req contract.BulkArchiveLettersRequest
	if !h.bindJSON(c, &req) {
		return
	}

	if len(req.LetterIDs) == 0 {
		h.respondError(c, http.StatusBadRequest, "at least one letter ID is required")
		return
	}

	resp, err := h.svc.BulkArchiveIncomingLetters(c.Request.Context(), req.LetterIDs)
	if err != nil {
		h.handleServiceError(c, err)
		return
	}

	h.respondSuccess(c, http.StatusOK, resp)
}
