package service

import (
	"context"
	"time"

	"eslogad-be/internal/contract"
	"eslogad-be/internal/entities"
	"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
}

type LetterOutgoingServiceImpl 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
	txManager                *repository.TxManager
}

func NewLetterOutgoingService(
	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,
	txManager *repository.TxManager,
) *LetterOutgoingServiceImpl {
	return &LetterOutgoingServiceImpl{
		db:                       db,
		letterRepo:               letterRepo,
		attachmentRepo:           attachmentRepo,
		recipientRepo:            recipientRepo,
		discussionRepo:           discussionRepo,
		discussionAttachmentRepo: discussionAttachmentRepo,
		activityLogRepo:          activityLogRepo,
		approvalFlowRepo:         approvalFlowRepo,
		approvalRepo:             approvalRepo,
		txManager:                txManager,
	}
}

func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, req *contract.CreateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) {
	letter := &entities.LetterOutgoing{
		Subject:               req.Subject,
		Description:           req.Description,
		PriorityID:            req.PriorityID,
		ReceiverInstitutionID: req.ReceiverInstitutionID,
		IssueDate:             req.IssueDate,
		Status:                entities.LetterOutgoingStatusDraft,
		ApprovalFlowID:        req.ApprovalFlowID,
		CreatedBy:             req.UserID,
	}

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

	err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.letterRepo.Create(txCtx, letter); err != nil {
			return err
		}

		if len(req.Recipients) > 0 {
			recipients := make([]entities.LetterOutgoingRecipient, len(req.Recipients))
			for i, r := range req.Recipients {
				recipients[i] = entities.LetterOutgoingRecipient{
					LetterID:             letter.ID,
					RecipientName:        r.Name,
					RecipientEmail:       r.Email,
					RecipientPosition:    r.Position,
					RecipientInstitution: r.Institution,
					IsPrimary:            r.IsPrimary,
				}
			}
			if err := s.recipientRepo.CreateBulk(txCtx, recipients); err != nil {
				return err
			}
		}

		if len(req.Attachments) > 0 {
			attachments := make([]entities.LetterOutgoingAttachment, len(req.Attachments))
			for i, a := range req.Attachments {
				attachments[i] = entities.LetterOutgoingAttachment{
					LetterID:   letter.ID,
					FileURL:    a.FileURL,
					FileName:   a.FileName,
					FileType:   a.FileType,
					UploadedBy: &req.UserID,
				}
			}
			if err := s.attachmentRepo.CreateBulk(txCtx, attachments); err != nil {
				return err
			}
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letter.ID,
			ActionType:  entities.LetterOutgoingActionCreated,
			ActorUserID: &req.UserID,
		}
		if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	result, err := s.letterRepo.Get(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.letterRepo.Get(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) {
	filter := repository.ListOutgoingLettersFilter{
		Status:                req.Status,
		Query:                 req.Query,
		CreatedBy:             req.CreatedBy,
		ReceiverInstitutionID: req.ReceiverInstitutionID,
		FromDate:              req.FromDate,
		ToDate:                req.ToDate,
	}

	letters, total, err := s.letterRepo.List(ctx, filter, req.Limit, req.Offset)
	if err != nil {
		return nil, err
	}

	items := make([]*contract.OutgoingLetterResponse, len(letters))
	for i, letter := range letters {
		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.letterRepo.Get(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.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.letterRepo.Update(txCtx, letter); err != nil {
			return err
		}

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

		return nil
	})

	if err != nil {
		return nil, err
	}

	result, err := s.letterRepo.Get(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.letterRepo.Get(ctx, id)
	if err != nil {
		return err
	}

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

	return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.letterRepo.SoftDelete(txCtx, id); err != nil {
			return err
		}

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

		return nil
	})
}

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

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

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

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

	flow, err := s.approvalFlowRepo.Get(ctx, *letter.ApprovalFlowID)
	if err != nil {
		return err
	}

	return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		approvals := make([]entities.LetterOutgoingApproval, len(flow.Steps))
		for i, step := range flow.Steps {
			approvals[i] = entities.LetterOutgoingApproval{
				LetterID: letterID,
				StepID:   step.ID,
				Status:   entities.ApprovalStatusPending,
			}
		}

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

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

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

		return nil
	})
}

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

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

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

	approvals, err := s.approvalRepo.ListByLetter(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
	}

	return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		now := time.Now()
		currentApproval.Status = entities.ApprovalStatusApproved
		currentApproval.ApproverID = &userID
		currentApproval.ActedAt = &now
		currentApproval.Remarks = req.Remarks

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

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

		if allApproved {
			if err := s.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusApproved); err != nil {
				return err
			}
		}

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

		return nil
	})
}

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

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

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

	approvals, err := s.approvalRepo.ListByLetter(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
	}

	return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		now := time.Now()
		currentApproval.Status = entities.ApprovalStatusRejected
		currentApproval.ApproverID = &userID
		currentApproval.ActedAt = &now
		currentApproval.Remarks = &req.Reason

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

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

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

		return nil
	})
}

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

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

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

	return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusSent); err != nil {
			return err
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActionType:  entities.LetterOutgoingActionSent,
			ActorUserID: &userID,
			FromStatus:  ptr(string(entities.LetterOutgoingStatusApproved)),
			ToStatus:    ptr(string(entities.LetterOutgoingStatusSent)),
		}
		if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

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

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

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

	return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusArchived); err != nil {
			return err
		}

		activityLog := &entities.LetterOutgoingActivityLog{
			LetterID:    letterID,
			ActionType:  entities.LetterOutgoingActionArchived,
			ActorUserID: &userID,
			FromStatus:  ptr(string(entities.LetterOutgoingStatusSent)),
			ToStatus:    ptr(string(entities.LetterOutgoingStatusArchived)),
		}
		if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
			return err
		}

		return nil
	})
}

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

	letter, err := s.letterRepo.Get(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,
			RecipientName:        r.Name,
			RecipientEmail:       r.Email,
			RecipientPosition:    r.Position,
			RecipientInstitution: r.Institution,
			IsPrimary:            r.IsPrimary,
		}
	}

	return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.recipientRepo.CreateBulk(txCtx, recipients); err != nil {
			return err
		}

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

		return nil
	})
}

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

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

	recipient := &entities.LetterOutgoingRecipient{
		ID:                   recipientID,
		RecipientName:        req.Name,
		RecipientEmail:       req.Email,
		RecipientPosition:    req.Position,
		RecipientInstitution: req.Institution,
		IsPrimary:            req.IsPrimary,
	}

	return s.recipientRepo.Update(ctx, recipient)
}

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

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

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

	return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.recipientRepo.Delete(txCtx, recipientID); err != nil {
			return err
		}

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

		return nil
	})
}

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

	letter, err := s.letterRepo.Get(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.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.attachmentRepo.CreateBulk(txCtx, attachments); err != nil {
			return err
		}

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

		return nil
	})
}

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

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

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

	return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.attachmentRepo.Delete(txCtx, attachmentID); err != nil {
			return err
		}

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

		return nil
	})
}

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
	}

	err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
		if err := s.discussionRepo.Create(txCtx, discussion); err != nil {
			return err
		}

		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,
				}
			}
			if err := s.discussionAttachmentRepo.CreateBulk(txCtx, attachments); err != nil {
				return err
			}
		}

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

		return nil
	})

	if err != nil {
		return nil, err
	}

	result, err := s.discussionRepo.Get(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.discussionRepo.Get(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.discussionRepo.Update(ctx, discussion)
}

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

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

	return s.discussionRepo.Delete(ctx, discussionID)
}

func getUserIDFromContext(ctx context.Context) uuid.UUID {
	return uuid.New()
}

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

func ptr(s string) *string {
	return &s
}

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 {
			resp.Recipients[i] = contract.OutgoingLetterRecipientResponse{
				ID:          recipient.ID,
				Name:        recipient.RecipientName,
				Email:       recipient.RecipientEmail,
				Position:    recipient.RecipientPosition,
				Institution: recipient.RecipientInstitution,
				IsPrimary:   recipient.IsPrimary,
				CreatedAt:   recipient.CreatedAt,
			}
		}
	}

	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,
				ApproverID: approval.ApproverID,
				Status:     string(approval.Status),
				Remarks:    approval.Remarks,
				ActedAt:    approval.ActedAt,
				CreatedAt:  approval.CreatedAt,
			}

			// Include step order if step is loaded
			if approval.Step != nil {
				approvalResp.StepOrder = approval.Step.StepOrder
			}

			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,
	}
}
