package service

import (
	"context"
	"fmt"
	"sort"
	"time"

	"eslogad-be/internal/appcontext"
	"eslogad-be/internal/contract"
	"eslogad-be/internal/entities"
	"eslogad-be/internal/processor"
	"eslogad-be/internal/repository"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

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

	GetLetterApprovalInfo(ctx context.Context, letterID uuid.UUID) (*contract.LetterApprovalInfoResponse, error)

	// GetLetterApprovals returns all approvals and their status for a letter
	GetLetterApprovals(ctx context.Context, letterID uuid.UUID) (*contract.GetLetterApprovalsResponse, error)

	// GetApprovalDiscussions returns both approvals and discussions for an outgoing letter
	GetApprovalDiscussions(ctx context.Context, letterID uuid.UUID) (*contract.OutgoingLetterApprovalDiscussionsResponse, error)

	// GetApprovalTimeline returns a chronological timeline of all events for a letter
	GetApprovalTimeline(ctx context.Context, letterID uuid.UUID) (*contract.ApprovalTimelineResponse, error)
}

type LetterOutgoingServiceImpl struct {
	processor           processor.LetterOutgoingProcessor
	txManager           *repository.TxManager
	validationProcessor processor.LetterValidationProcessor
	creationProcessor   processor.LetterCreationProcessor
	approvalProcessor   processor.LetterApprovalProcessor
	attachmentProcessor processor.LetterAttachmentProcessor
	recipientProcessor  processor.LetterOutgoingRecipientProcessor
	activityProcessor   processor.LetterActivityProcessor
}

func NewLetterOutgoingService(
	processor processor.LetterOutgoingProcessor,
	txManager *repository.TxManager,
	validationProcessor processor.LetterValidationProcessor,
	creationProcessor processor.LetterCreationProcessor,
	approvalProcessor processor.LetterApprovalProcessor,
	attachmentProcessor processor.LetterAttachmentProcessor,
	recipientProcessor processor.LetterOutgoingRecipientProcessor,
	activityProcessor processor.LetterActivityProcessor,
) *LetterOutgoingServiceImpl {
	return &LetterOutgoingServiceImpl{
		processor:           processor,
		txManager:           txManager,
		validationProcessor: validationProcessor,
		creationProcessor:   creationProcessor,
		approvalProcessor:   approvalProcessor,
		attachmentProcessor: attachmentProcessor,
		recipientProcessor:  recipientProcessor,
		activityProcessor:   activityProcessor,
	}
}

func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, req *contract.CreateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) {
	departmentID := getDepartmentIDFromContext(ctx)
	userID := getUserIDFromContext(ctx)

	// Create letter entity
	letter := &entities.LetterOutgoing{
		Subject:               req.Subject,
		Description:           req.Description,
		PriorityID:            req.PriorityID,
		ReceiverInstitutionID: req.ReceiverInstitutionID,
		IssueDate:             req.IssueDate,
		CreatedBy:             userID,
	}

	if req.ReferenceNumber != nil {
		letter.ReferenceNumber = req.ReferenceNumber
	}

	// Prepare attachments
	var attachments []entities.LetterOutgoingAttachment
	if len(req.Attachments) > 0 {
		attachments = make([]entities.LetterOutgoingAttachment, len(req.Attachments))
		for i, a := range req.Attachments {
			attachments[i] = entities.LetterOutgoingAttachment{
				FileURL:    a.FileURL,
				FileName:   a.FileName,
				FileType:   a.FileType,
				UploadedBy: &userID,
			}
		}
	}

	// Execute creation with transaction in service layer
	err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		// Step 1: Validate letter
		if err := s.validationProcessor.ValidateCreateOutgoingLetter(txCtx, letter); err != nil {
			return err
		}

		// Step 2: Prepare letter for creation (assign approval flow, set initial status)
		if err := s.creationProcessor.PrepareLetterForCreation(txCtx, letter, departmentID); err != nil {
			return err
		}

		// Step 3: Generate letter number
		if err := s.creationProcessor.GenerateLetterNumber(txCtx, letter); err != nil {
			return err
		}

		// Step 4: Create the letter
		if err := s.creationProcessor.CreateLetter(txCtx, letter); err != nil {
			return err
		}

		// Step 5: Create approval steps if needed
		if err := s.approvalProcessor.CreateApprovalSteps(txCtx, letter); err != nil {
			return err
		}

		// Step 6: Create initial recipients (approval workflow users + department members)
		if err := s.recipientProcessor.CreateInitialRecipients(txCtx, letter, departmentID); err != nil {
			return err
		}

		// Step 7: Create attachments
		if err := s.attachmentProcessor.CreateAttachments(txCtx, letter.ID, attachments); err != nil {
			return err
		}

		// Step 8: Log the activity
		return s.activityProcessor.LogActivity(txCtx, letter.ID, entities.LetterOutgoingActionCreated, userID, nil)
	})

	if err != nil {
		return nil, err
	}

	// Get the created letter with all relationships
	result, err := s.processor.GetOutgoingLetterByID(ctx, letter.ID)
	if err != nil {
		return nil, err
	}

	return transformLetterToResponse(result), nil
}

func (s *LetterOutgoingServiceImpl) GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*contract.OutgoingLetterResponse, error) {
	letter, err := s.processor.GetOutgoingLetterByID(ctx, id)
	if err != nil {
		return nil, err
	}

	return transformLetterToResponse(letter), nil
}

func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req *contract.ListOutgoingLettersRequest) (*contract.ListOutgoingLettersResponse, error) {
	// Extract user context from gin context
	appCtx := appcontext.FromGinContext(ctx)
	userID := appCtx.UserID
	departmentID := appCtx.DepartmentID

	offset := (req.Page - 1) * req.Limit
	if offset < 0 {
		offset = 0
	}

	filter := repository.ListOutgoingLettersFilter{
		CreatedBy:             req.CreatedBy,
		DepartmentID:          req.DepartmentID,
		ReceiverInstitutionID: req.ReceiverInstitutionID,
		PriorityID:            req.PriorityID,
		UserID:                &userID,
	}

	if departmentID != uuid.Nil {
		filter.DepartmentID = &departmentID
	}

	if req.Status != "" {
		filter.Status = &req.Status
	}
	if req.Query != "" {
		filter.Query = &req.Query
	}
	if req.SortBy != "" {
		filter.SortBy = &req.SortBy
	}
	if req.SortOrder != "" {
		filter.SortOrder = &req.SortOrder
	}

	if req.FromDate != "" {
		if date, err := time.Parse("2006-01-02", req.FromDate); err == nil {
			filter.FromDate = &date
		}
	}

	if req.ToDate != "" {
		if date, err := time.Parse("2006-01-02", req.ToDate); err == nil {
			endOfDay := date.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
			filter.ToDate = &endOfDay
		}
	}

	filter.IsArchived = req.IsArchived

	// Get raw letters data
	letters, total, err := s.processor.ListOutgoingLetters(ctx, filter, req.Limit, offset)
	if err != nil {
		return nil, err
	}

	// Collect IDs for batch loading
	letterIDs := make([]uuid.UUID, len(letters))
	priorityIDs := make(map[uuid.UUID]bool)
	institutionIDs := make(map[uuid.UUID]bool)
	
	for i, letter := range letters {
		letterIDs[i] = letter.ID
		if letter.PriorityID != nil {
			priorityIDs[*letter.PriorityID] = true
		}
		if letter.ReceiverInstitutionID != nil {
			institutionIDs[*letter.ReceiverInstitutionID] = true
		}
	}

	// Convert maps to slices
	priorityIDSlice := make([]uuid.UUID, 0, len(priorityIDs))
	for id := range priorityIDs {
		priorityIDSlice = append(priorityIDSlice, id)
	}
	
	institutionIDSlice := make([]uuid.UUID, 0, len(institutionIDs))
	for id := range institutionIDs {
		institutionIDSlice = append(institutionIDSlice, id)
	}

	// Parallel batch loading
	type batchResult struct {
		attachments  map[uuid.UUID][]entities.LetterOutgoingAttachment
		recipients   map[uuid.UUID][]entities.LetterOutgoingRecipient
		priorities   map[uuid.UUID]*entities.Priority
		institutions map[uuid.UUID]*entities.Institution
		err          error
	}

	result := batchResult{}
	errChan := make(chan error, 4)
	
	// Load attachments
	go func() {
		result.attachments, err = s.processor.GetBatchAttachments(ctx, letterIDs)
		errChan <- err
	}()
	
	// Load recipients  
	go func() {
		result.recipients, err = s.processor.GetBatchRecipients(ctx, letterIDs)
		errChan <- err
	}()
	
	// Load priorities
	go func() {
		result.priorities, err = s.processor.GetBatchPriorities(ctx, priorityIDSlice)
		errChan <- err
	}()
	
	// Load institutions
	go func() {
		result.institutions, err = s.processor.GetBatchInstitutions(ctx, institutionIDSlice)
		errChan <- err
	}()
	
	// Wait for all goroutines and check for errors
	for i := 0; i < 4; i++ {
		if err := <-errChan; err != nil {
			return nil, err
		}
	}

	// Transform letters with batch loaded data
	items := make([]*contract.OutgoingLetterResponse, len(letters))
	for i, letter := range letters {
		// Attach batch loaded data to letter
		if attachments, ok := result.attachments[letter.ID]; ok {
			letter.Attachments = attachments
		}
		if recipients, ok := result.recipients[letter.ID]; ok {
			letter.Recipients = recipients
		}
		if letter.PriorityID != nil {
			if priority, ok := result.priorities[*letter.PriorityID]; ok {
				letter.Priority = priority
			}
		}
		if letter.ReceiverInstitutionID != nil {
			if institution, ok := result.institutions[*letter.ReceiverInstitutionID]; ok {
				letter.ReceiverInstitution = institution
			}
		}
		
		items[i] = transformLetterToResponse(&letter)
	}

	return &contract.ListOutgoingLettersResponse{
		Items: items,
		Total: total,
	}, nil
}

func (s *LetterOutgoingServiceImpl) UpdateOutgoingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, id)
	if err != nil {
		return nil, err
	}

	if letter.Status != entities.LetterOutgoingStatusDraft {
		return nil, gorm.ErrInvalidData
	}

	if req.Subject != nil {
		letter.Subject = *req.Subject
	}
	if req.Description != nil {
		letter.Description = req.Description
	}
	if req.PriorityID != nil {
		letter.PriorityID = req.PriorityID
	}
	if req.ReceiverInstitutionID != nil {
		letter.ReceiverInstitutionID = req.ReceiverInstitutionID
	}
	if req.IssueDate != nil {
		letter.IssueDate = *req.IssueDate
	}
	if req.ReferenceNumber != nil {
		letter.ReferenceNumber = req.ReferenceNumber
	}

	err = s.processor.UpdateOutgoingLetter(ctx, letter, userID)
	if err != nil {
		return nil, err
	}

	result, err := s.processor.GetOutgoingLetterByID(ctx, id)
	if err != nil {
		return nil, err
	}

	return transformLetterToResponse(result), nil
}

func (s *LetterOutgoingServiceImpl) DeleteOutgoingLetter(ctx context.Context, id uuid.UUID) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, id)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusDraft {
		return gorm.ErrInvalidData
	}

	return s.processor.DeleteOutgoingLetter(ctx, id, userID)
}

func (s *LetterOutgoingServiceImpl) SubmitForApproval(ctx context.Context, letterID uuid.UUID) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusDraft {
		return gorm.ErrInvalidData
	}

	if letter.ApprovalFlowID == nil {
		return gorm.ErrInvalidData
	}

	return s.processor.ProcessApprovalSubmission(ctx, letterID, *letter.ApprovalFlowID, userID)
}

func (s *LetterOutgoingServiceImpl) ApproveOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.ApproveLetterRequest) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusPendingApproval {
		return gorm.ErrInvalidData
	}

	approvals, err := s.processor.GetApprovalsByLetter(ctx, letterID)
	if err != nil {
		return err
	}

	var currentApproval *entities.LetterOutgoingApproval
	for i := range approvals {
		if approvals[i].Status == entities.ApprovalStatusPending {
			step := approvals[i].Step
			if (step.ApproverUserID != nil && *step.ApproverUserID == userID) ||
				(step.ApproverRoleID != nil && userHasRole(ctx, *step.ApproverRoleID)) {
				currentApproval = &approvals[i]
				break
			}
		}
	}

	if currentApproval == nil {
		return gorm.ErrInvalidData
	}

	currentApproval.Remarks = req.Remarks

	allApproved := true
	for _, approval := range approvals {
		if approval.ID != currentApproval.ID && approval.Status == entities.ApprovalStatusPending {
			allApproved = false
			break
		}
	}

	return s.processor.ProcessApproval(ctx, letterID, currentApproval, userID, allApproved)
}

func (s *LetterOutgoingServiceImpl) RejectOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.RejectLetterRequest) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusPendingApproval {
		return gorm.ErrInvalidData
	}

	approvals, err := s.processor.GetApprovalsByLetter(ctx, letterID)
	if err != nil {
		return err
	}

	var currentApproval *entities.LetterOutgoingApproval
	for i := range approvals {
		if approvals[i].Status == entities.ApprovalStatusPending {
			step := approvals[i].Step
			if (step.ApproverUserID != nil && *step.ApproverUserID == userID) ||
				(step.ApproverRoleID != nil && userHasRole(ctx, *step.ApproverRoleID)) {
				currentApproval = &approvals[i]
				break
			}
		}
	}

	if currentApproval == nil {
		return gorm.ErrInvalidData
	}

	currentApproval.Remarks = &req.Reason

	return s.processor.ProcessRejection(ctx, letterID, currentApproval, userID)
}

func (s *LetterOutgoingServiceImpl) SendOutgoingLetter(ctx context.Context, letterID uuid.UUID) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusApproved {
		return gorm.ErrInvalidData
	}

	fromStatus := string(entities.LetterOutgoingStatusApproved)
	toStatus := string(entities.LetterOutgoingStatusSent)
	return s.processor.UpdateLetterStatus(ctx, letterID, entities.LetterOutgoingStatusSent, userID, &fromStatus, &toStatus)
}

func (s *LetterOutgoingServiceImpl) ArchiveOutgoingLetter(ctx context.Context, letterID uuid.UUID) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusSent {
		return gorm.ErrInvalidData
	}

	fromStatus := string(entities.LetterOutgoingStatusSent)
	toStatus := string(entities.LetterOutgoingStatusArchived)
	return s.processor.UpdateLetterStatus(ctx, letterID, entities.LetterOutgoingStatusArchived, userID, &fromStatus, &toStatus)
}

func (s *LetterOutgoingServiceImpl) AddRecipients(ctx context.Context, letterID uuid.UUID, req *contract.AddRecipientsRequest) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusDraft {
		return gorm.ErrInvalidData
	}

	recipients := make([]entities.LetterOutgoingRecipient, len(req.Recipients))
	for i, r := range req.Recipients {
		recipients[i] = entities.LetterOutgoingRecipient{
			LetterID:     letterID,
			UserID:       r.UserID,
			DepartmentID: r.DepartmentID,
			IsPrimary:    r.IsPrimary,
			Status:       r.Status,
			Flag:         r.Flag,
			IsArchived:   r.IsArchived,
		}
	}

	return s.processor.AddRecipients(ctx, letterID, recipients, userID)
}

func (s *LetterOutgoingServiceImpl) UpdateRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID, req *contract.UpdateRecipientRequest) error {
	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusDraft {
		return gorm.ErrInvalidData
	}

	recipient := &entities.LetterOutgoingRecipient{
		ID:        recipientID,
		IsPrimary: req.IsPrimary,
	}

	if req.UserID != nil {
		recipient.UserID = req.UserID
	}
	if req.DepartmentID != nil {
		recipient.DepartmentID = req.DepartmentID
	}
	if req.Status != nil {
		recipient.Status = *req.Status
	}
	if req.Flag != nil {
		recipient.Flag = req.Flag
	}
	if req.IsArchived != nil {
		recipient.IsArchived = *req.IsArchived
	}

	return s.processor.UpdateRecipient(ctx, recipient)
}

func (s *LetterOutgoingServiceImpl) RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusDraft {
		return gorm.ErrInvalidData
	}

	return s.processor.RemoveRecipient(ctx, letterID, recipientID, userID)
}

func (s *LetterOutgoingServiceImpl) AddAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusDraft {
		return gorm.ErrInvalidData
	}

	attachments := make([]entities.LetterOutgoingAttachment, len(req.Attachments))
	for i, a := range req.Attachments {
		attachments[i] = entities.LetterOutgoingAttachment{
			LetterID:   letterID,
			FileURL:    a.FileURL,
			FileName:   a.FileName,
			FileType:   a.FileType,
			UploadedBy: &userID,
		}
	}

	return s.processor.AddAttachments(ctx, letterID, attachments, userID)
}

func (s *LetterOutgoingServiceImpl) RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error {
	userID := getUserIDFromContext(ctx)

	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return err
	}

	if letter.Status != entities.LetterOutgoingStatusDraft {
		return gorm.ErrInvalidData
	}

	return s.processor.RemoveAttachment(ctx, letterID, attachmentID, userID)
}

func (s *LetterOutgoingServiceImpl) CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateDiscussionRequest) (*contract.DiscussionResponse, error) {
	userID := getUserIDFromContext(ctx)

	discussion := &entities.LetterOutgoingDiscussion{
		LetterID: letterID,
		ParentID: req.ParentID,
		UserID:   userID,
		Message:  req.Message,
	}

	if req.Mentions != nil {
		discussion.Mentions = req.Mentions
	}

	var attachments []entities.LetterOutgoingDiscussionAttachment
	if len(req.Attachments) > 0 {
		attachments = make([]entities.LetterOutgoingDiscussionAttachment, len(req.Attachments))
		for i, a := range req.Attachments {
			attachments[i] = entities.LetterOutgoingDiscussionAttachment{
				DiscussionID: discussion.ID,
				FileURL:      a.FileURL,
				FileName:     a.FileName,
				FileType:     a.FileType,
				UploadedBy:   &userID,
			}
		}
	}

	err := s.processor.CreateDiscussion(ctx, discussion, attachments, userID)

	if err != nil {
		return nil, err
	}

	result, err := s.processor.GetDiscussionByID(ctx, discussion.ID)
	if err != nil {
		return nil, err
	}

	return transformDiscussionToResponse(result), nil
}

func (s *LetterOutgoingServiceImpl) UpdateDiscussion(ctx context.Context, discussionID uuid.UUID, req *contract.UpdateDiscussionRequest) error {
	discussion, err := s.processor.GetDiscussionByID(ctx, discussionID)
	if err != nil {
		return err
	}

	userID := getUserIDFromContext(ctx)
	if discussion.UserID != userID {
		return gorm.ErrInvalidData
	}

	discussion.Message = req.Message
	if req.Mentions != nil {
		discussion.Mentions = req.Mentions
	}

	return s.processor.UpdateDiscussion(ctx, discussion)
}

func (s *LetterOutgoingServiceImpl) DeleteDiscussion(ctx context.Context, discussionID uuid.UUID) error {
	discussion, err := s.processor.GetDiscussionByID(ctx, discussionID)
	if err != nil {
		return err
	}

	userID := getUserIDFromContext(ctx)
	if discussion.UserID != userID {
		return gorm.ErrInvalidData
	}

	return s.processor.DeleteDiscussion(ctx, discussionID)
}

func (s *LetterOutgoingServiceImpl) GetLetterApprovalInfo(ctx context.Context, letterID uuid.UUID) (*contract.LetterApprovalInfoResponse, error) {
	userID := getUserIDFromContext(ctx)

	// Verify letter exists
	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return nil, err
	}

	// Get all approvals for this letter
	approvals, err := s.processor.GetApprovalsByLetter(ctx, letterID)
	if err != nil {
		return nil, err
	}

	// Group approvals by step order to understand the workflow
	approvalsByStep := make(map[int][]entities.LetterOutgoingApproval)
	for _, approval := range approvals {
		approvalsByStep[approval.StepOrder] = append(approvalsByStep[approval.StepOrder], approval)
	}

	// Find the current active step (lowest step order with pending approvals)
	var currentStepOrder int = -1
	var userApproval *entities.LetterOutgoingApproval
	var isApproverOnActiveStep bool
	var canApprove bool

	// Find the minimum step order that has pending approvals
	for stepOrder, stepApprovals := range approvalsByStep {
		hasPending := false
		for _, approval := range stepApprovals {
			if approval.Status == entities.ApprovalStatusPending {
				hasPending = true
				// Check if this user is an approver for this pending approval
				if approval.ApproverID != nil && *approval.ApproverID == userID {
					if currentStepOrder == -1 || stepOrder < currentStepOrder {
						currentStepOrder = stepOrder
						userApproval = &approval
						isApproverOnActiveStep = true
					}
				}
			}
		}
		// Track the lowest pending step
		if hasPending && (currentStepOrder == -1 || stepOrder < currentStepOrder) {
			currentStepOrder = stepOrder
		}
	}

	// User can approve if they have a pending approval on the current active step
	if isApproverOnActiveStep && userApproval != nil && userApproval.Status == entities.ApprovalStatusPending {
		canApprove = true
	}

	// Build actions based on eligibility
	var actions []contract.ApprovalAction
	if canApprove && userApproval != nil {
		actions = []contract.ApprovalAction{
			{
				Type:   "APPROVE",
				Href:   fmt.Sprintf("/api/v1/letters/outgoing/%s/approve", letterID),
				Method: "POST",
			},
			{
				Type:   "REJECT",
				Href:   fmt.Sprintf("/api/v1/letters/outgoing/%s/reject", letterID),
				Method: "POST",
			},
		}
	}

	// Determine overall decision status
	decisionStatus := "PENDING"

	// Check if all required approvals are completed
	allCompleted := true
	hasRejection := false
	for _, approval := range approvals {
		// Check required approvals only
		if approval.IsRequired {
			if approval.Status == entities.ApprovalStatusPending || approval.Status == entities.ApprovalStatusNotStarted {
				allCompleted = false
			}
			if approval.Status == entities.ApprovalStatusRejected {
				hasRejection = true
			}
		}
	}

	if hasRejection {
		decisionStatus = "REJECTED"
	} else if allCompleted {
		decisionStatus = "COMPLETED"
	} else if letter.Status == entities.LetterOutgoingStatusPendingApproval {
		decisionStatus = "PENDING"
	}

	// Determine notes visibility
	notesVisibility := "READONLY"
	if canApprove {
		notesVisibility = "FULL"
	}

	info := &contract.LetterApprovalInfoResponse{
		IsApproverOnActiveStep: isApproverOnActiveStep,
		DecisionStatus:         decisionStatus,
		CanApprove:             canApprove,
		Actions:                actions,
		NotesVisibility:        notesVisibility,
	}

	return info, nil
}

func (s *LetterOutgoingServiceImpl) GetLetterApprovals(ctx context.Context, letterID uuid.UUID) (*contract.GetLetterApprovalsResponse, error) {
	// Get letter details
	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return nil, err
	}

	// Get all approvals for this letter
	approvals, err := s.processor.GetApprovalsByLetter(ctx, letterID)
	if err != nil {
		return nil, err
	}

	// Sort approvals by step order and parallel group
	sort.Slice(approvals, func(i, j int) bool {
		if approvals[i].StepOrder != approvals[j].StepOrder {
			return approvals[i].StepOrder < approvals[j].StepOrder
		}
		return approvals[i].ParallelGroup < approvals[j].ParallelGroup
	})

	// Transform to response format
	approvalResponses := make([]contract.EnhancedOutgoingLetterApprovalResponse, 0, len(approvals))
	totalSteps := 0
	currentStep := 0
	stepOrdersSeen := make(map[int]bool)

	for _, approval := range approvals {
		// Count unique step orders for total steps
		if !stepOrdersSeen[approval.StepOrder] {
			stepOrdersSeen[approval.StepOrder] = true
			totalSteps++
		}

		// Determine current step (lowest step with pending/not_started status)
		if approval.Status == entities.ApprovalStatusPending && (currentStep == 0 || approval.StepOrder < currentStep) {
			currentStep = approval.StepOrder
		}

		approvalResp := contract.EnhancedOutgoingLetterApprovalResponse{
			ID:            approval.ID,
			LetterID:      approval.LetterID,
			StepID:        approval.StepID,
			StepOrder:     approval.StepOrder,
			ParallelGroup: approval.ParallelGroup,
			IsRequired:    approval.IsRequired,
			ApproverID:    approval.ApproverID,
			Status:        string(approval.Status),
			Remarks:       approval.Remarks,
			ActedAt:       approval.ActedAt,
			CreatedAt:     approval.CreatedAt,
		}

		// Add step details if available
		if approval.Step != nil {
			approvalResp.Step = &contract.ApprovalFlowStepResponse{
				ID:            approval.Step.ID,
				StepOrder:     approval.Step.StepOrder,
				ParallelGroup: approval.Step.ParallelGroup,
				Required:      approval.Step.Required,
				CreatedAt:     approval.Step.CreatedAt,
				UpdatedAt:     approval.Step.UpdatedAt,
			}

			// Add approver role if available
			if approval.Step.ApproverRole != nil {
				approvalResp.Step.ApproverRole = &contract.RoleResponse{
					ID:   approval.Step.ApproverRole.ID,
					Name: approval.Step.ApproverRole.Name,
					Code: approval.Step.ApproverRole.Code,
				}
			}

			// Add approver user if available
			if approval.Step.ApproverUser != nil {
				approvalResp.Step.ApproverUser = &contract.UserResponse{
					ID:    approval.Step.ApproverUser.ID,
					Name:  approval.Step.ApproverUser.Name,
					Email: approval.Step.ApproverUser.Email,
				}
			}
		}

		// Add approver details if available
		if approval.Approver != nil {
			approvalResp.Approver = &contract.UserResponse{
				ID:    approval.Approver.ID,
				Name:  approval.Approver.Name,
				Email: approval.Approver.Email,
			}
		}

		approvalResponses = append(approvalResponses, approvalResp)
	}

	// If no current step found but there are approvals, check if all are completed
	if currentStep == 0 && len(approvals) > 0 {
		allCompleted := true
		for _, approval := range approvals {
			if approval.IsRequired && approval.Status != entities.ApprovalStatusApproved {
				allCompleted = false
				break
			}
		}
		if allCompleted {
			currentStep = totalSteps // All steps completed
		}
	}

	response := &contract.GetLetterApprovalsResponse{
		LetterID:     letter.ID,
		LetterNumber: letter.LetterNumber,
		LetterStatus: string(letter.Status),
		TotalSteps:   totalSteps,
		CurrentStep:  currentStep,
		Approvals:    approvalResponses,
	}

	return response, nil
}

func getUserIDFromContext(ctx context.Context) uuid.UUID {
	appCtx := appcontext.FromGinContext(ctx)
	if appCtx != nil {
		return appCtx.UserID
	}
	return uuid.New()
}

func getDepartmentIDFromContext(ctx context.Context) uuid.UUID {
	appCtx := appcontext.FromGinContext(ctx)
	if appCtx != nil {
		return appCtx.DepartmentID
	}
	return uuid.Nil
}

func userHasRole(ctx context.Context, roleID uuid.UUID) bool {
	return false
}

func (s *LetterOutgoingServiceImpl) GetApprovalDiscussions(ctx context.Context, letterID uuid.UUID) (*contract.OutgoingLetterApprovalDiscussionsResponse, error) {
	// Get the letter with all related data
	letter, err := s.processor.GetOutgoingLetterWithDetails(ctx, letterID)
	if err != nil {
		return nil, err
	}

	// Transform approvals
	approvals := make([]contract.EnhancedOutgoingLetterApprovalResponse, 0, len(letter.Approvals))
	for _, approval := range letter.Approvals {
		approvalResp := contract.EnhancedOutgoingLetterApprovalResponse{
			ID:            approval.ID,
			LetterID:      approval.LetterID,
			StepID:        approval.StepID,
			StepOrder:     approval.StepOrder,
			ParallelGroup: approval.ParallelGroup,
			IsRequired:    approval.IsRequired,
			ApproverID:    approval.ApproverID,
			Status:        string(approval.Status),
			Remarks:       approval.Remarks,
			ActedAt:       approval.ActedAt,
			CreatedAt:     approval.CreatedAt,
		}

		// Add step details if available
		if approval.Step != nil {
			approvalResp.Step = &contract.ApprovalFlowStepResponse{
				ID:            approval.Step.ID,
				StepOrder:     approval.Step.StepOrder,
				ParallelGroup: approval.Step.ParallelGroup,
				Required:      approval.Step.Required,
				CreatedAt:     approval.Step.CreatedAt,
				UpdatedAt:     approval.Step.UpdatedAt,
			}

			if approval.Step.ApproverRoleID != nil {
				approvalResp.Step.ApproverRoleID = approval.Step.ApproverRoleID
			}
			if approval.Step.ApproverUserID != nil {
				approvalResp.Step.ApproverUserID = approval.Step.ApproverUserID
			}

			// Add role information if available
			if approval.Step.ApproverRole != nil {
				approvalResp.Step.ApproverRole = &contract.RoleResponse{
					ID:   approval.Step.ApproverRole.ID,
					Name: approval.Step.ApproverRole.Name,
					Code: approval.Step.ApproverRole.Code,
				}
			}

			// Add user information if available
			if approval.Step.ApproverUser != nil {
				approvalResp.Step.ApproverUser = &contract.UserResponse{
					ID:    approval.Step.ApproverUser.ID,
					Name:  approval.Step.ApproverUser.Name,
					Email: approval.Step.ApproverUser.Email,
				}
			}
		}

		// Add approver details if available
		if approval.Approver != nil {
			approvalResp.Approver = &contract.UserResponse{
				ID:    approval.Approver.ID,
				Name:  approval.Approver.Name,
				Email: approval.Approver.Email,
			}

			// Add profile if available
			if approval.Approver.Profile != nil {
				approvalResp.Approver.Profile = &contract.UserProfileResponse{
					UserID:      approval.Approver.Profile.UserID,
					FullName:    approval.Approver.Profile.FullName,
					DisplayName: approval.Approver.Profile.DisplayName,
					Phone:       approval.Approver.Profile.Phone,
					AvatarURL:   approval.Approver.Profile.AvatarURL,
					JobTitle:    approval.Approver.Profile.JobTitle,
					EmployeeNo:  approval.Approver.Profile.EmployeeNo,
					Bio:         approval.Approver.Profile.Bio,
					Timezone:    approval.Approver.Profile.Timezone,
					Locale:      approval.Approver.Profile.Locale,
				}
			}
		}

		approvals = append(approvals, approvalResp)
	}

	// Transform discussions
	discussions := make([]contract.OutgoingLetterDiscussionResponse, 0, len(letter.Discussions))
	for _, discussion := range letter.Discussions {
		// Extract mentioned user IDs from mentions
		mentionedUserIDs := extractMentionedUserIDs(discussion.Mentions)

		discussionResp := contract.OutgoingLetterDiscussionResponse{
			ID:        discussion.ID,
			LetterID:  discussion.LetterID,
			ParentID:  discussion.ParentID,
			UserID:    discussion.UserID,
			Message:   discussion.Message,
			Mentions:  discussion.Mentions,
			CreatedAt: discussion.CreatedAt,
			UpdatedAt: discussion.UpdatedAt,
			EditedAt:  discussion.EditedAt,
		}

		// Add user details if available
		if discussion.User != nil {
			discussionResp.User = &contract.UserResponse{
				ID:        discussion.User.ID,
				Name:      discussion.User.Name,
				Email:     discussion.User.Email,
				IsActive:  discussion.User.IsActive,
				CreatedAt: discussion.User.CreatedAt,
				UpdatedAt: discussion.User.UpdatedAt,
			}

			// Add profile if available
			if discussion.User.Profile != nil {
				discussionResp.User.Profile = &contract.UserProfileResponse{
					UserID:      discussion.User.Profile.UserID,
					FullName:    discussion.User.Profile.FullName,
					DisplayName: discussion.User.Profile.DisplayName,
					Phone:       discussion.User.Profile.Phone,
					AvatarURL:   discussion.User.Profile.AvatarURL,
					JobTitle:    discussion.User.Profile.JobTitle,
					EmployeeNo:  discussion.User.Profile.EmployeeNo,
					Bio:         discussion.User.Profile.Bio,
					Timezone:    discussion.User.Profile.Timezone,
					Locale:      discussion.User.Profile.Locale,
				}
			}
		}

		// Get mentioned users details
		if len(mentionedUserIDs) > 0 {
			mentionedUsers, _ := s.processor.GetUsersByIDs(ctx, mentionedUserIDs)
			for _, user := range mentionedUsers {
				mentionedUserResp := contract.UserResponse{
					ID:        user.ID,
					Name:      user.Name,
					Email:     user.Email,
					IsActive:  user.IsActive,
					CreatedAt: user.CreatedAt,
					UpdatedAt: user.UpdatedAt,
				}

				if user.Profile != nil {
					mentionedUserResp.Profile = &contract.UserProfileResponse{
						UserID:      user.Profile.UserID,
						FullName:    user.Profile.FullName,
						DisplayName: user.Profile.DisplayName,
						Timezone:    user.Profile.Timezone,
						Locale:      user.Profile.Locale,
					}
				}

				discussionResp.MentionedUsers = append(discussionResp.MentionedUsers, mentionedUserResp)
			}
		}

		// Add attachments if available
		for _, attachment := range discussion.Attachments {
			attachmentResp := contract.OutgoingLetterDiscussionAttachmentResponse{
				ID:           attachment.ID,
				DiscussionID: attachment.DiscussionID,
				FileURL:      attachment.FileURL,
				FileName:     attachment.FileName,
				FileType:     attachment.FileType,
				UploadedBy:   attachment.UploadedBy,
				UploadedAt:   attachment.UploadedAt,
			}
			discussionResp.Attachments = append(discussionResp.Attachments, attachmentResp)
		}

		discussions = append(discussions, discussionResp)
	}

	return &contract.OutgoingLetterApprovalDiscussionsResponse{
		Approvals:   approvals,
		Discussions: discussions,
	}, nil
}

// Helper function to extract user IDs from mentions
func extractMentionedUserIDs(mentions map[string]interface{}) []uuid.UUID {
	var userIDs []uuid.UUID

	if mentions == nil {
		return userIDs
	}

	if userIDsInterface, ok := mentions["user_ids"]; ok {
		if userIDsList, ok := userIDsInterface.([]interface{}); ok {
			for _, id := range userIDsList {
				if idStr, ok := id.(string); ok {
					if userID, err := uuid.Parse(idStr); err == nil {
						userIDs = append(userIDs, userID)
					}
				}
			}
		}
	}

	return userIDs
}

func transformLetterToResponse(letter *entities.LetterOutgoing) *contract.OutgoingLetterResponse {
	resp := &contract.OutgoingLetterResponse{
		ID:                    letter.ID,
		LetterNumber:          letter.LetterNumber,
		ReferenceNumber:       letter.ReferenceNumber,
		Subject:               letter.Subject,
		Description:           letter.Description,
		PriorityID:            letter.PriorityID,
		ReceiverInstitutionID: letter.ReceiverInstitutionID,
		IssueDate:             letter.IssueDate,
		Status:                string(letter.Status),
		ApprovalFlowID:        letter.ApprovalFlowID,
		CreatedBy:             letter.CreatedBy,
		CreatedAt:             letter.CreatedAt,
		UpdatedAt:             letter.UpdatedAt,
	}

	if letter.Priority != nil {
		resp.Priority = &contract.PriorityResponse{
			ID:        letter.Priority.ID.String(),
			Name:      letter.Priority.Name,
			Level:     letter.Priority.Level,
			CreatedAt: letter.Priority.CreatedAt,
			UpdatedAt: letter.Priority.UpdatedAt,
		}
	}

	if letter.ReceiverInstitution != nil {
		resp.ReceiverInstitution = &contract.InstitutionResponse{
			ID:            letter.ReceiverInstitution.ID.String(),
			Name:          letter.ReceiverInstitution.Name,
			Type:          string(letter.ReceiverInstitution.Type),
			Address:       letter.ReceiverInstitution.Address,
			ContactPerson: letter.ReceiverInstitution.ContactPerson,
			Phone:         letter.ReceiverInstitution.Phone,
			Email:         letter.ReceiverInstitution.Email,
			CreatedAt:     letter.ReceiverInstitution.CreatedAt,
			UpdatedAt:     letter.ReceiverInstitution.UpdatedAt,
		}
	}

	if len(letter.Recipients) > 0 {
		resp.Recipients = make([]contract.OutgoingLetterRecipientResponse, len(letter.Recipients))
		for i, recipient := range letter.Recipients {
			recipResp := contract.OutgoingLetterRecipientResponse{
				ID:           recipient.ID,
				LetterID:     recipient.LetterID,
				UserID:       recipient.UserID,
				DepartmentID: recipient.DepartmentID,
				IsPrimary:    recipient.IsPrimary,
				Status:       recipient.Status,
				ReadAt:       recipient.ReadAt,
				Flag:         recipient.Flag,
				IsArchived:   recipient.IsArchived,
				CreatedAt:    recipient.CreatedAt,
			}

			if recipient.User != nil {
				recipResp.User = &contract.UserResponse{
					ID:    recipient.User.ID,
					Name:  recipient.User.Name,
					Email: recipient.User.Email,
				}
			}

			if recipient.Department != nil {
				recipResp.Department = &contract.DepartmentResponse{
					ID:   recipient.Department.ID,
					Name: recipient.Department.Name,
					Code: recipient.Department.Code,
				}
			}

			resp.Recipients[i] = recipResp
		}
	}

	if len(letter.Attachments) > 0 {
		resp.Attachments = make([]contract.OutgoingLetterAttachmentResponse, len(letter.Attachments))
		for i, attachment := range letter.Attachments {
			resp.Attachments[i] = contract.OutgoingLetterAttachmentResponse{
				ID:         attachment.ID,
				FileURL:    attachment.FileURL,
				FileName:   attachment.FileName,
				FileType:   attachment.FileType,
				UploadedAt: attachment.UploadedAt,
			}
		}
	}

	if len(letter.Approvals) > 0 {
		resp.Approvals = make([]contract.OutgoingLetterApprovalResponse, len(letter.Approvals))
		for i, approval := range letter.Approvals {
			approvalResp := contract.OutgoingLetterApprovalResponse{
				ID:            approval.ID,
				StepOrder:     approval.StepOrder,
				ParallelGroup: approval.ParallelGroup,
				IsRequired:    approval.IsRequired,
				ApproverID:    approval.ApproverID,
				Status:        string(approval.Status),
				Remarks:       approval.Remarks,
				ActedAt:       approval.ActedAt,
				CreatedAt:     approval.CreatedAt,
			}

			resp.Approvals[i] = approvalResp
		}
	}

	return resp
}

func transformDiscussionToResponse(discussion *entities.LetterOutgoingDiscussion) *contract.DiscussionResponse {
	return &contract.DiscussionResponse{
		ID:        discussion.ID,
		UserID:    discussion.UserID,
		Message:   discussion.Message,
		CreatedAt: discussion.CreatedAt,
		UpdatedAt: discussion.UpdatedAt,
	}
}

// GetApprovalTimeline generates a chronological timeline of all events for a letter
func (s *LetterOutgoingServiceImpl) GetApprovalTimeline(ctx context.Context, letterID uuid.UUID) (*contract.ApprovalTimelineResponse, error) {
	// Get letter details
	letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
	if err != nil {
		return nil, err
	}

	// Get approvals and discussions
	approvalDiscussions, err := s.GetApprovalDiscussions(ctx, letterID)
	if err != nil {
		return nil, err
	}

	// Create timeline events
	timeline := make([]contract.TimelineEvent, 0)

	// Add letter creation event
	timeline = append(timeline, contract.TimelineEvent{
		ID:          letter.ID.String(),
		Type:        "submission",
		Timestamp:   letter.CreatedAt,
		Actor:       nil, // Could add creator info here if needed
		Action:      "created",
		Description: "Letter was created",
		Status:      "created",
	})

	// Add approval events
	for _, approval := range approvalDiscussions.Approvals {
		if approval.ActedAt != nil {
			eventType := "approval"
			action := "approved"
			status := "approved"
			
			if approval.Status == "rejected" {
				eventType = "rejection"
				action = "rejected"
				status = "rejected"
			} else if approval.Status == "pending" {
				continue // Skip pending approvals as they haven't happened yet
			}

			description := fmt.Sprintf("Step %d: %s by %s", 
				approval.StepOrder,
				action,
				getApproverName(approval.Approver))

			timeline = append(timeline, contract.TimelineEvent{
				ID:          approval.ID.String(),
				Type:        eventType,
				Timestamp:   *approval.ActedAt,
				Actor:       approval.Approver,
				Action:      action,
				Description: description,
				Status:      status,
				StepOrder:   approval.StepOrder,
				Message:     getLetterStringValue(approval.Remarks),
				Data:        approval,
			})
		}
	}

	// Add discussion events
	for _, discussion := range approvalDiscussions.Discussions {
		timeline = append(timeline, contract.TimelineEvent{
			ID:          discussion.ID.String(),
			Type:        "discussion",
			Timestamp:   discussion.CreatedAt,
			Actor:       discussion.User,
			Action:      "commented",
			Description: fmt.Sprintf("%s added a comment", getUserName(discussion.User)),
			Message:     discussion.Message,
			Data:        discussion,
		})
	}

	// Sort timeline by timestamp
	sort.Slice(timeline, func(i, j int) bool {
		return timeline[i].Timestamp.Before(timeline[j].Timestamp)
	})

	// Calculate summary statistics
	summary := s.calculateTimelineSummary(letter, approvalDiscussions.Approvals, timeline)

	return &contract.ApprovalTimelineResponse{
		LetterID:     letter.ID,
		LetterNumber: letter.LetterNumber,
		Subject:      letter.Subject,
		Status:       string(letter.Status),
		CreatedAt:    letter.CreatedAt,
		Timeline:     timeline,
		Summary:      summary,
	}, nil
}

func (s *LetterOutgoingServiceImpl) calculateTimelineSummary(
	letter *entities.LetterOutgoing,
	approvals []contract.EnhancedOutgoingLetterApprovalResponse,
	timeline []contract.TimelineEvent,
) contract.TimelineSummary {
	totalSteps := 0
	completedSteps := 0
	pendingSteps := 0
	currentStep := 0
	
	// Count unique step orders
	stepMap := make(map[int]string)
	for _, approval := range approvals {
		if _, exists := stepMap[approval.StepOrder]; !exists {
			stepMap[approval.StepOrder] = approval.Status
			totalSteps++
		}
		
		switch approval.Status {
		case "approved":
			if stepMap[approval.StepOrder] == "approved" {
				completedSteps++
				currentStep = approval.StepOrder + 1
			}
		case "pending":
			pendingSteps++
			if currentStep == 0 {
				currentStep = approval.StepOrder
			}
		}
	}

	// Calculate duration
	totalDuration := ""
	averageStepTime := ""
	
	if len(timeline) > 0 {
		lastEvent := timeline[len(timeline)-1]
		duration := lastEvent.Timestamp.Sub(letter.CreatedAt)
		totalDuration = formatDuration(duration)
		
		if completedSteps > 0 {
			avgDuration := duration / time.Duration(completedSteps)
			averageStepTime = formatDuration(avgDuration)
		}
	}

	status := "in_progress"
	if letter.Status == entities.LetterOutgoingStatusApproved {
		status = "completed"
	} else if letter.Status == "rejected" {
		status = "rejected"
	}

	return contract.TimelineSummary{
		TotalSteps:      totalSteps,
		CompletedSteps:  completedSteps,
		PendingSteps:    pendingSteps,
		CurrentStep:     currentStep,
		TotalDuration:   totalDuration,
		AverageStepTime: averageStepTime,
		Status:          status,
	}
}

func getApproverName(user *contract.UserResponse) string {
	if user == nil {
		return "Unknown"
	}
	if user.Name != "" {
		return user.Name
	}
	return user.Email
}

func getUserName(user *contract.UserResponse) string {
	if user == nil {
		return "Unknown"
	}
	if user.Name != "" {
		return user.Name
	}
	return user.Email
}

func getLetterStringValue(s *string) string {
	if s == nil {
		return ""
	}
	return *s
}

func formatDuration(d time.Duration) string {
	days := int(d.Hours() / 24)
	hours := int(d.Hours()) % 24
	minutes := int(d.Minutes()) % 60
	
	if days > 0 {
		return fmt.Sprintf("%dd %dh %dm", days, hours, minutes)
	} else if hours > 0 {
		return fmt.Sprintf("%dh %dm", hours, minutes)
	}
	return fmt.Sprintf("%dm", minutes)
}

func ApplyLetterFilterOverrides(ctx context.Context, filter *repository.ListOutgoingLettersFilter) {
	appCtx := appcontext.FromGinContext(ctx)
	if appCtx == nil {
		return
	}

	isSuperAdmin := false
	if appCtx.UserRole == "superadmin" || appCtx.UserRole == "admin" {
		isSuperAdmin = true
	}

	if !isSuperAdmin && appCtx.UserID != uuid.Nil {
		filter.UserID = &appCtx.UserID
	}
}

func (s *LetterOutgoingServiceImpl) BulkArchiveOutgoingLetters(ctx context.Context, letterIDs []uuid.UUID) (*contract.BulkArchiveLettersResponse, error) {
	archivedCount, err := s.processor.BulkArchiveOutgoingLetters(ctx, letterIDs)
	if err != nil {
		return nil, err
	}
	
	return &contract.BulkArchiveLettersResponse{
		Success:       true,
		Message:       "Letters archived successfully",
		ArchivedCount: int(archivedCount),
	}, nil
}
