package processor

import (
	"context"
	"time"

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

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

type LetterOutgoingProcessor interface {
	CreateOutgoingLetter(ctx context.Context, letter *entities.LetterOutgoing, attachments []entities.LetterOutgoingAttachment, userID, departmentID uuid.UUID) error
	GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*entities.LetterOutgoing, error)
	ListOutgoingLetters(ctx context.Context, filter repository.ListOutgoingLettersFilter, limit, offset int) ([]entities.LetterOutgoing, int64, error)
	UpdateOutgoingLetter(ctx context.Context, letter *entities.LetterOutgoing, userID uuid.UUID) error
	DeleteOutgoingLetter(ctx context.Context, id uuid.UUID, userID uuid.UUID) error

	UpdateLetterStatus(ctx context.Context, letterID uuid.UUID, status entities.LetterOutgoingStatus, userID uuid.UUID, fromStatus, toStatus *string) error

	ProcessApprovalSubmission(ctx context.Context, letterID uuid.UUID, approvalFlowID uuid.UUID, userID uuid.UUID) error
	ProcessApproval(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID, allApproved bool) error
	ProcessRejection(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID) error

	AddRecipients(ctx context.Context, letterID uuid.UUID, recipients []entities.LetterOutgoingRecipient, userID uuid.UUID) error
	UpdateRecipient(ctx context.Context, recipient *entities.LetterOutgoingRecipient) error
	RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID, userID uuid.UUID) error

	AddAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingAttachment, userID uuid.UUID) error
	RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID, userID uuid.UUID) error

	CreateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion, attachments []entities.LetterOutgoingDiscussionAttachment, userID uuid.UUID) error
	GetDiscussionByID(ctx context.Context, id uuid.UUID) (*entities.LetterOutgoingDiscussion, error)
	UpdateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion) error
	DeleteDiscussion(ctx context.Context, id uuid.UUID) error

	GetApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error)
	GetApprovalFlow(ctx context.Context, flowID uuid.UUID) (*entities.ApprovalFlow, error)

	// GetOutgoingLetterWithDetails fetches letter with all related data
	GetOutgoingLetterWithDetails(ctx context.Context, letterID uuid.UUID) (*entities.LetterOutgoing, error)
	GetUsersByIDs(ctx context.Context, userIDs []uuid.UUID) ([]entities.User, error)
	BulkArchiveOutgoingLetters(ctx context.Context, letterIDs []uuid.UUID) (int64, error)
	
	// Batch loading methods for efficient querying
	GetBatchAttachments(ctx context.Context, letterIDs []uuid.UUID) (map[uuid.UUID][]entities.LetterOutgoingAttachment, error)
	GetBatchRecipients(ctx context.Context, letterIDs []uuid.UUID) (map[uuid.UUID][]entities.LetterOutgoingRecipient, error)
	GetBatchPriorities(ctx context.Context, priorityIDs []uuid.UUID) (map[uuid.UUID]*entities.Priority, error)
	GetBatchInstitutions(ctx context.Context, institutionIDs []uuid.UUID) (map[uuid.UUID]*entities.Institution, error)
}

type LetterOutgoingProcessorImpl struct {
	db                       *gorm.DB
	letterRepo               *repository.LetterOutgoingRepository
	attachmentRepo           *repository.LetterOutgoingAttachmentRepository
	recipientRepo            *repository.LetterOutgoingRecipientRepository
	discussionRepo           *repository.LetterOutgoingDiscussionRepository
	discussionAttachmentRepo *repository.LetterOutgoingDiscussionAttachmentRepository
	activityLogRepo          *repository.LetterOutgoingActivityLogRepository
	approvalFlowRepo         *repository.ApprovalFlowRepository
	approvalRepo             *repository.LetterOutgoingApprovalRepository
	numberGenerator          *LetterNumberGeneratorImpl
	txManager                *repository.TxManager
	priorityRepo             *repository.PriorityRepository
	institutionRepo          *repository.InstitutionRepository
}

func NewLetterOutgoingProcessor(
	db *gorm.DB,
	letterRepo *repository.LetterOutgoingRepository,
	attachmentRepo *repository.LetterOutgoingAttachmentRepository,
	recipientRepo *repository.LetterOutgoingRecipientRepository,
	discussionRepo *repository.LetterOutgoingDiscussionRepository,
	discussionAttachmentRepo *repository.LetterOutgoingDiscussionAttachmentRepository,
	activityLogRepo *repository.LetterOutgoingActivityLogRepository,
	approvalFlowRepo *repository.ApprovalFlowRepository,
	approvalRepo *repository.LetterOutgoingApprovalRepository,
	numberGenerator *LetterNumberGeneratorImpl,
	txManager *repository.TxManager,
	priorityRepo *repository.PriorityRepository,
	institutionRepo *repository.InstitutionRepository,
) *LetterOutgoingProcessorImpl {
	return &LetterOutgoingProcessorImpl{
		db:                       db,
		letterRepo:               letterRepo,
		attachmentRepo:           attachmentRepo,
		recipientRepo:            recipientRepo,
		discussionRepo:           discussionRepo,
		discussionAttachmentRepo: discussionAttachmentRepo,
		activityLogRepo:          activityLogRepo,
		approvalFlowRepo:         approvalFlowRepo,
		approvalRepo:             approvalRepo,
		numberGenerator:          numberGenerator,
		txManager:                txManager,
		priorityRepo:             priorityRepo,
		institutionRepo:          institutionRepo,
	}
}

func (p *LetterOutgoingProcessorImpl) CreateOutgoingLetter(ctx context.Context, letter *entities.LetterOutgoing, attachments []entities.LetterOutgoingAttachment, userID, departmentID uuid.UUID) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		// Step 1: Assign approval flow from department if not provided
		if err := p.assignApprovalFlowFromDepartment(txCtx, letter, departmentID); err != nil {
			// Log error but continue - approval flow is optional
		}

		// Step 2: Set status based on approval flow
		if letter.ApprovalFlowID != nil {
			letter.Status = entities.LetterOutgoingStatusPendingApproval
		} else {
			letter.Status = entities.LetterOutgoingStatusApproved
		}

		// Step 3: Generate and assign letter number
		if err := p.assignLetterNumber(txCtx, letter); err != nil {
			return err
		}

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

		// Step 5: Create recipients from approval flow
		if err := p.createRecipientsFromApprovalFlow(txCtx, letter); err != nil {
			return err
		}

		// Step 6: Create attachments
		if err := p.createAttachments(txCtx, letter.ID, attachments); err != nil {
			return err
		}

		// Step 7: Log the activity
		return p.logActivity(txCtx, letter.ID, entities.LetterOutgoingActionCreated, userID)
	})
}

func (p *LetterOutgoingProcessorImpl) assignApprovalFlowFromDepartment(ctx context.Context, letter *entities.LetterOutgoing, departmentID uuid.UUID) error {
	if letter.ApprovalFlowID != nil || departmentID == uuid.Nil {
		return nil
	}

	flow, err := p.approvalFlowRepo.GetByDepartment(ctx, departmentID)
	if err != nil {
		return err
	}

	if flow != nil {
		letter.ApprovalFlowID = &flow.ID
	}

	return nil
}

func (p *LetterOutgoingProcessorImpl) assignLetterNumber(ctx context.Context, letter *entities.LetterOutgoing) error {
	letterNumber, err := p.numberGenerator.GenerateNumber(
		ctx,
		contract.SettingOutgoingLetterPrefix,
		contract.SettingOutgoingLetterSequence,
		"ESLO",
	)

	if err != nil {
		return err
	}

	letter.LetterNumber = letterNumber

	return nil
}

func (p *LetterOutgoingProcessorImpl) createRecipientsFromApprovalFlow(ctx context.Context, letter *entities.LetterOutgoing) error {
	if letter.ApprovalFlowID == nil {
		return nil
	}

	flow, err := p.approvalFlowRepo.Get(ctx, *letter.ApprovalFlowID)
	if err != nil || flow == nil || len(flow.Steps) == 0 {
		return err
	}

	// Find the minimum step order (first step)
	minStepOrder := flow.Steps[0].StepOrder
	for _, step := range flow.Steps {
		if step.StepOrder < minStepOrder {
			minStepOrder = step.StepOrder
		}
	}

	// Create all approval steps in letter_outgoing_approvals
	var approvals []entities.LetterOutgoingApproval
	for _, step := range flow.Steps {
		approval := entities.LetterOutgoingApproval{
			LetterID:      letter.ID,
			StepID:        step.ID,
			StepOrder:     step.StepOrder,
			ParallelGroup: step.ParallelGroup,
			IsRequired:    step.Required,
			ApproverID:    step.ApproverUserID,
		}

		// Set status based on step order
		if step.StepOrder == minStepOrder {
			// First step(s) are set to pending
			approval.Status = entities.ApprovalStatusPending
		} else {
			// All other steps are set to not_started
			approval.Status = entities.ApprovalStatusNotStarted
		}

		approvals = append(approvals, approval)
	}

	// Bulk create all approvals
	if len(approvals) > 0 {
		if err := p.approvalRepo.CreateBulk(ctx, approvals); err != nil {
			return err
		}
	}

	// Also create recipients from the first step (for backward compatibility)
	var recipients []entities.LetterOutgoingRecipient
	for i, step := range flow.Steps {
		// Only process steps with the minimum step order (first step)
		if step.StepOrder != minStepOrder {
			continue
		}

		recipient := p.createRecipientFromApprovalStep(step, letter.ID)
		if recipient != nil {
			// Mark the first recipient as primary
			if i == 0 {
				recipient.IsPrimary = true
			} else {
				recipient.IsPrimary = false
			}
			recipients = append(recipients, *recipient)
		}
	}

	// Bulk create all recipients if any
	if len(recipients) > 0 {
		return p.recipientRepo.CreateBulk(ctx, recipients)
	}

	return nil
}

// createRecipientFromApprovalStep creates a recipient from an approval flow step
func (p *LetterOutgoingProcessorImpl) createRecipientFromApprovalStep(step entities.ApprovalFlowStep, letterID uuid.UUID) *entities.LetterOutgoingRecipient {
	recipient := &entities.LetterOutgoingRecipient{
		LetterID:   letterID,
		Status:     "pending",
		IsArchived: false,
		UserID:     &step.ApproverUser.ID,
	}

	return recipient
}

// createAttachments creates letter attachments
func (p *LetterOutgoingProcessorImpl) createAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingAttachment) error {
	if len(attachments) == 0 {
		return nil
	}

	// Update letter IDs for all attachments
	for i := range attachments {
		attachments[i].LetterID = letterID
	}

	return p.attachmentRepo.CreateBulk(ctx, attachments)
}

// logActivity logs an activity for the letter
func (p *LetterOutgoingProcessorImpl) logActivity(ctx context.Context, letterID uuid.UUID, actionType string, userID uuid.UUID) error {
	activityLog := &entities.LetterOutgoingActivityLog{
		LetterID:    letterID,
		ActionType:  actionType,
		ActorUserID: &userID,
	}
	return p.activityLogRepo.Create(ctx, activityLog)
}

func (p *LetterOutgoingProcessorImpl) GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*entities.LetterOutgoing, error) {
	return p.letterRepo.Get(ctx, id)
}

func (p *LetterOutgoingProcessorImpl) ListOutgoingLetters(ctx context.Context, filter repository.ListOutgoingLettersFilter, limit, offset int) ([]entities.LetterOutgoing, int64, error) {
	return p.letterRepo.List(ctx, filter, limit, offset)
}

func (p *LetterOutgoingProcessorImpl) UpdateOutgoingLetter(ctx context.Context, letter *entities.LetterOutgoing, userID uuid.UUID) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := p.letterRepo.Update(txCtx, letter); err != nil {
			return err
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letter.ID,
			ActionType:  entities.LetterOutgoingActionUpdated,
			ActorUserID: &userID,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) DeleteOutgoingLetter(ctx context.Context, id uuid.UUID, userID uuid.UUID) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := p.letterRepo.SoftDelete(txCtx, id); err != nil {
			return err
		}

		letter, _ := p.letterRepo.Get(txCtx, id)
		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letter.ID,
			ActionType:  entities.LetterOutgoingActionDeleted,
			ActorUserID: &userID,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) UpdateLetterStatus(ctx context.Context, letterID uuid.UUID, status entities.LetterOutgoingStatus, userID uuid.UUID, fromStatus, toStatus *string) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := p.letterRepo.UpdateStatus(txCtx, letterID, status); err != nil {
			return err
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActorUserID: &userID,
			FromStatus:  fromStatus,
			ToStatus:    toStatus,
		}

		switch status {
		case entities.LetterOutgoingStatusPendingApproval:
			activityLog.ActionType = entities.LetterOutgoingActionSubmittedApproval
		case entities.LetterOutgoingStatusApproved:
			activityLog.ActionType = entities.LetterOutgoingActionApproved
		case entities.LetterOutgoingStatusSent:
			activityLog.ActionType = entities.LetterOutgoingActionSent
		case entities.LetterOutgoingStatusArchived:
			activityLog.ActionType = entities.LetterOutgoingActionArchived
		default:
			activityLog.ActionType = entities.LetterOutgoingActionUpdated
		}

		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) ProcessApprovalSubmission(ctx context.Context, letterID uuid.UUID, approvalFlowID uuid.UUID, userID uuid.UUID) error {
	flow, err := p.approvalFlowRepo.Get(ctx, approvalFlowID)
	if err != nil {
		return err
	}

	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		// Find the minimum step order (first step)
		minStepOrder := flow.Steps[0].StepOrder
		for _, step := range flow.Steps {
			if step.StepOrder < minStepOrder {
				minStepOrder = step.StepOrder
			}
		}

		approvals := make([]entities.LetterOutgoingApproval, len(flow.Steps))
		for i, step := range flow.Steps {
			approvals[i] = entities.LetterOutgoingApproval{
				LetterID:      letterID,
				StepID:        step.ID,
				StepOrder:     step.StepOrder,
				ParallelGroup: step.ParallelGroup,
				IsRequired:    step.Required,
				ApproverID:    step.ApproverUserID,
				Status:        entities.ApprovalStatusPending,
			}

			// Set status based on step order
			if step.StepOrder == minStepOrder {
				approvals[i].Status = entities.ApprovalStatusPending
			} else {
				approvals[i].Status = entities.ApprovalStatusNotStarted
			}
		}

		if err := p.approvalRepo.CreateBulk(txCtx, approvals); err != nil {
			return err
		}

		// Add first step approvers as recipients
		existingRecipients, err := p.recipientRepo.ListByLetter(txCtx, letterID)
		if err != nil {
			return err
		}

		// Create a map of existing user IDs for quick lookup
		existingUserIDs := make(map[uuid.UUID]bool)
		for _, recipient := range existingRecipients {
			if recipient.UserID != nil {
				existingUserIDs[*recipient.UserID] = true
			}
		}

		// Add approvers from the first step as recipients
		for _, approval := range approvals {
			if approval.StepOrder == minStepOrder && approval.ApproverID != nil {
				if !existingUserIDs[*approval.ApproverID] {
					newRecipient := entities.LetterOutgoingRecipient{
						LetterID:   letterID,
						UserID:     approval.ApproverID,
						IsPrimary:  false,
						Status:     "unread",
						IsArchived: false,
					}
					if err := p.recipientRepo.Create(txCtx, &newRecipient); err != nil {
						return err
					}
					existingUserIDs[*approval.ApproverID] = true
				}
			}
		}

		if err := p.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusPendingApproval); err != nil {
			return err
		}

		fromStatus := string(entities.LetterOutgoingStatusDraft)
		toStatus := string(entities.LetterOutgoingStatusPendingApproval)
		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActionType:  entities.LetterOutgoingActionSubmittedApproval,
			ActorUserID: &userID,
			FromStatus:  &fromStatus,
			ToStatus:    &toStatus,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) ProcessApproval(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID, allApproved bool) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		now := time.Now()
		approval.Status = entities.ApprovalStatusApproved
		approval.ApproverID = &userID
		approval.ActedAt = &now

		if err := p.approvalRepo.Update(txCtx, approval); err != nil {
			return err
		}

		allApprovals, err := p.approvalRepo.ListByLetter(txCtx, letterID)
		if err != nil {
			return err
		}

		approvalsByStep := make(map[int][]entities.LetterOutgoingApproval)
		for _, a := range allApprovals {
			approvalsByStep[a.StepOrder] = append(approvalsByStep[a.StepOrder], a)
		}

		currentStepCompleted := true
		for _, a := range approvalsByStep[approval.StepOrder] {
			if a.IsRequired && a.Status != entities.ApprovalStatusApproved {
				currentStepCompleted = false
				break
			}
		}

		// If current step is completed, activate the next step and add approvers as recipients
		if currentStepCompleted {
			nextStepOrder := approval.StepOrder + 1
			if nextStepApprovals, exists := approvalsByStep[nextStepOrder]; exists {
				currentRecipients, err := p.recipientRepo.ListByLetter(txCtx, letterID)
				if err != nil {
					return err
				}

				existingUserIDs := make(map[uuid.UUID]bool)
				for _, recipient := range currentRecipients {
					if recipient.UserID != nil {
						existingUserIDs[*recipient.UserID] = true
					}
				}

				for _, nextApproval := range nextStepApprovals {

					if nextApproval.Status == entities.ApprovalStatusNotStarted {
						nextApproval.Status = entities.ApprovalStatusPending
						if err := p.approvalRepo.Update(txCtx, &nextApproval); err != nil {
							return err
						}
					}

					if nextApproval.ApproverID != nil && !existingUserIDs[*nextApproval.ApproverID] {
						newRecipient := entities.LetterOutgoingRecipient{
							LetterID:   letterID,
							UserID:     nextApproval.ApproverID,
							IsPrimary:  false,
							Status:     "unread",
							IsArchived: false,
						}
						if err := p.recipientRepo.Create(txCtx, &newRecipient); err != nil {
							return err
						}

						existingUserIDs[*nextApproval.ApproverID] = true
					}
				}
			}
		}

		// Check if all required approvals are completed
		allRequiredApproved := true
		for _, a := range allApprovals {
			if a.IsRequired && a.Status != entities.ApprovalStatusApproved {
				allRequiredApproved = false
				break
			}
		}

		// Update letter status if all required approvals are done
		if allRequiredApproved {
			if err := p.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusApproved); err != nil {
				return err
			}
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActionType:  entities.LetterOutgoingActionApproved,
			ActorUserID: &userID,
			TargetID:    &approval.ID,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) ProcessRejection(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		now := time.Now()
		approval.Status = entities.ApprovalStatusRejected
		approval.ApproverID = &userID
		approval.ActedAt = &now

		if err := p.approvalRepo.Update(txCtx, approval); err != nil {
			return err
		}

		if err := p.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusDraft); err != nil {
			return err
		}

		fromStatus := string(entities.LetterOutgoingStatusPendingApproval)
		toStatus := string(entities.LetterOutgoingStatusDraft)
		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActionType:  entities.LetterOutgoingActionRejected,
			ActorUserID: &userID,
			TargetID:    &approval.ID,
			FromStatus:  &fromStatus,
			ToStatus:    &toStatus,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) AddRecipients(ctx context.Context, letterID uuid.UUID, recipients []entities.LetterOutgoingRecipient, userID uuid.UUID) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := p.recipientRepo.CreateBulk(txCtx, recipients); err != nil {
			return err
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActionType:  entities.LetterOutgoingActionRecipientAdded,
			ActorUserID: &userID,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) UpdateRecipient(ctx context.Context, recipient *entities.LetterOutgoingRecipient) error {
	return p.recipientRepo.Update(ctx, recipient)
}

func (p *LetterOutgoingProcessorImpl) RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID, userID uuid.UUID) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := p.recipientRepo.Delete(txCtx, recipientID); err != nil {
			return err
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActionType:  entities.LetterOutgoingActionRecipientRemoved,
			ActorUserID: &userID,
			TargetID:    &recipientID,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) AddAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingAttachment, userID uuid.UUID) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := p.attachmentRepo.CreateBulk(txCtx, attachments); err != nil {
			return err
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActionType:  entities.LetterOutgoingActionAttachmentAdded,
			ActorUserID: &userID,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID, userID uuid.UUID) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := p.attachmentRepo.Delete(txCtx, attachmentID); err != nil {
			return err
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActionType:  entities.LetterOutgoingActionAttachmentRemoved,
			ActorUserID: &userID,
			TargetID:    &attachmentID,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) CreateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion, attachments []entities.LetterOutgoingDiscussionAttachment, userID uuid.UUID) error {
	return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := p.discussionRepo.Create(txCtx, discussion); err != nil {
			return err
		}

		if len(attachments) > 0 {
			if err := p.discussionAttachmentRepo.CreateBulk(txCtx, attachments); err != nil {
				return err
			}
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    discussion.LetterID,
			ActionType:  entities.LetterOutgoingActionDiscussionAdded,
			ActorUserID: &userID,
			TargetID:    &discussion.ID,
		}
		if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

func (p *LetterOutgoingProcessorImpl) GetDiscussionByID(ctx context.Context, id uuid.UUID) (*entities.LetterOutgoingDiscussion, error) {
	return p.discussionRepo.Get(ctx, id)
}

func (p *LetterOutgoingProcessorImpl) UpdateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion) error {
	return p.discussionRepo.Update(ctx, discussion)
}

func (p *LetterOutgoingProcessorImpl) DeleteDiscussion(ctx context.Context, id uuid.UUID) error {
	return p.discussionRepo.Delete(ctx, id)
}

func (p *LetterOutgoingProcessorImpl) GetApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error) {
	return p.approvalRepo.ListByLetter(ctx, letterID)
}

func (p *LetterOutgoingProcessorImpl) GetApprovalFlow(ctx context.Context, flowID uuid.UUID) (*entities.ApprovalFlow, error) {
	return p.approvalFlowRepo.Get(ctx, flowID)
}

// GetOutgoingLetterWithDetails fetches letter with all related data including approvals, discussions, and users
func (p *LetterOutgoingProcessorImpl) GetOutgoingLetterWithDetails(ctx context.Context, letterID uuid.UUID) (*entities.LetterOutgoing, error) {
	letter, err := p.letterRepo.GetWithRelations(ctx, letterID, []string{
		"Priority",
		"ReceiverInstitution",
		"Creator",
		"Creator.Profile",
		"ApprovalFlow",
		"ApprovalFlow.Steps",
		"ApprovalFlow.Steps.ApproverRole",
		"ApprovalFlow.Steps.ApproverUser",
		"ApprovalFlow.Steps.ApproverUser.Profile",
		"Recipients",
		"Recipients.User",
		"Recipients.User.Profile",
		"Recipients.Department",
		"Attachments",
		"Approvals",
		"Approvals.Step",
		"Approvals.Step.ApproverRole",
		"Approvals.Step.ApproverUser",
		"Approvals.Step.ApproverUser.Profile",
		"Approvals.Approver",
		"Approvals.Approver.Profile",
		"Discussions",
		"Discussions.User",
		"Discussions.User.Profile",
		"Discussions.Attachments",
		"ActivityLogs",
	})

	if err != nil {
		return nil, err
	}

	return letter, nil
}

// GetUsersByIDs fetches users by their IDs
func (p *LetterOutgoingProcessorImpl) GetUsersByIDs(ctx context.Context, userIDs []uuid.UUID) ([]entities.User, error) {
	if len(userIDs) == 0 {
		return []entities.User{}, nil
	}

	var users []entities.User
	err := p.db.WithContext(ctx).
		Preload("Profile").
		Where("id IN ?", userIDs).
		Find(&users).Error

	if err != nil {
		return nil, err
	}

	return users, nil
}

func (p *LetterOutgoingProcessorImpl) BulkArchiveOutgoingLetters(ctx context.Context, letterIDs []uuid.UUID) (int64, error) {
	return p.letterRepo.BulkArchive(ctx, letterIDs)
}

// GetBatchAttachments fetches attachments for multiple letters in a single query
func (p *LetterOutgoingProcessorImpl) GetBatchAttachments(ctx context.Context, letterIDs []uuid.UUID) (map[uuid.UUID][]entities.LetterOutgoingAttachment, error) {
	if p.attachmentRepo == nil || len(letterIDs) == 0 {
		return make(map[uuid.UUID][]entities.LetterOutgoingAttachment), nil
	}
	return p.attachmentRepo.ListByLetterIDs(ctx, letterIDs)
}

// GetBatchRecipients fetches recipients for multiple letters in a single query
func (p *LetterOutgoingProcessorImpl) GetBatchRecipients(ctx context.Context, letterIDs []uuid.UUID) (map[uuid.UUID][]entities.LetterOutgoingRecipient, error) {
	if p.recipientRepo == nil || len(letterIDs) == 0 {
		return make(map[uuid.UUID][]entities.LetterOutgoingRecipient), nil
	}
	return p.recipientRepo.ListByLetterIDs(ctx, letterIDs)
}

// GetBatchPriorities fetches priorities by IDs in a single query
func (p *LetterOutgoingProcessorImpl) GetBatchPriorities(ctx context.Context, priorityIDs []uuid.UUID) (map[uuid.UUID]*entities.Priority, error) {
	if p.priorityRepo == nil || len(priorityIDs) == 0 {
		return make(map[uuid.UUID]*entities.Priority), nil
	}
	return p.priorityRepo.GetByIDs(ctx, priorityIDs)
}

// GetBatchInstitutions fetches institutions by IDs in a single query
func (p *LetterOutgoingProcessorImpl) GetBatchInstitutions(ctx context.Context, institutionIDs []uuid.UUID) (map[uuid.UUID]*entities.Institution, error) {
	if p.institutionRepo == nil || len(institutionIDs) == 0 {
		return make(map[uuid.UUID]*entities.Institution), nil
	}
	return p.institutionRepo.GetByIDs(ctx, institutionIDs)
}
