package processor

import (
	"context"

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

	"github.com/google/uuid"
)

// LetterOutgoingRecipientProcessor handles all recipient-related operations for outgoing letters
type LetterOutgoingRecipientProcessor interface {
	// CreateRecipients creates multiple recipients for a letter
	CreateRecipients(ctx context.Context, letterID uuid.UUID, recipients []entities.LetterOutgoingRecipient) error
	
	// CreateInitialRecipients creates recipients from both approval workflow and department members
	CreateInitialRecipients(ctx context.Context, letter *entities.LetterOutgoing, creatorDepartmentID uuid.UUID) error
	
	// UpdateRecipient updates a single recipient's information
	UpdateRecipient(ctx context.Context, recipient *entities.LetterOutgoingRecipient) error
	
	// RemoveRecipient removes a recipient from a letter
	RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID) error
	
	// GetRecipientsByLetterID retrieves all recipients for a specific letter
	GetRecipientsByLetterID(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingRecipient, error)
}

type LetterOutgoingRecipientProcessorImpl struct {
	recipientRepo    *repository.LetterOutgoingRecipientRepository
	approvalFlowRepo *repository.ApprovalFlowRepository
	userDeptRepo     *repository.UserDepartmentRepository
}

func NewLetterOutgoingRecipientProcessor(
	recipientRepo *repository.LetterOutgoingRecipientRepository,
	approvalFlowRepo *repository.ApprovalFlowRepository,
	userDeptRepo *repository.UserDepartmentRepository,
) *LetterOutgoingRecipientProcessorImpl {
	return &LetterOutgoingRecipientProcessorImpl{
		recipientRepo:    recipientRepo,
		approvalFlowRepo: approvalFlowRepo,
		userDeptRepo:     userDeptRepo,
	}
}

// CreateRecipients creates multiple recipients for a letter
func (p *LetterOutgoingRecipientProcessorImpl) CreateRecipients(
	ctx context.Context, 
	letterID uuid.UUID, 
	recipients []entities.LetterOutgoingRecipient,
) error {
	if len(recipients) == 0 {
		return nil
	}

	// Ensure all recipients have the correct letter ID and default status
	for i := range recipients {
		recipients[i].LetterID = letterID
		if recipients[i].Status == "" {
			recipients[i].Status = "pending"
		}
	}

	return p.recipientRepo.CreateBulk(ctx, recipients)
}

// CreateInitialRecipients creates the initial set of recipients for an outgoing letter
// It combines:
// 1. Approvers from the approval workflow (if exists)
// 2. All active users from the letter creator's department
func (p *LetterOutgoingRecipientProcessorImpl) CreateInitialRecipients(
	ctx context.Context, 
	letter *entities.LetterOutgoing, 
	creatorDepartmentID uuid.UUID,
) error {
	// Track unique users to avoid duplicates
	uniqueUsers := make(map[uuid.UUID]bool)
	var allRecipients []entities.LetterOutgoingRecipient

	// Step 1: Add recipients from approval workflow
	approvalRecipients := p.collectApprovalWorkflowRecipients(ctx, letter, uniqueUsers)
	allRecipients = append(allRecipients, approvalRecipients...)

	// Step 2: Add all users from the creator's department
	departmentRecipients := p.collectDepartmentRecipients(ctx, letter.ID, creatorDepartmentID, uniqueUsers)
	allRecipients = append(allRecipients, departmentRecipients...)

	// Step 3: Mark the first recipient as primary and save all
	if len(allRecipients) > 0 {
		allRecipients[0].IsPrimary = true
		return p.recipientRepo.CreateBulk(ctx, allRecipients)
	}

	return nil
}

// collectApprovalWorkflowRecipients gathers all users who are approvers in the workflow
func (p *LetterOutgoingRecipientProcessorImpl) collectApprovalWorkflowRecipients(
	ctx context.Context,
	letter *entities.LetterOutgoing,
	existingUsers map[uuid.UUID]bool,
) []entities.LetterOutgoingRecipient {
	var recipients []entities.LetterOutgoingRecipient
	
	// If no approval workflow is assigned, return empty
	if letter.ApprovalFlowID == nil {
		return recipients
	}

	// Fetch the approval workflow
	approvalFlow, err := p.approvalFlowRepo.Get(ctx, *letter.ApprovalFlowID)
	if err != nil || approvalFlow == nil || len(approvalFlow.Steps) == 0 {
		return recipients
	}

	// Add each approver as a recipient
	for _, step := range approvalFlow.Steps {
		if step.ApproverUserID == nil {
			continue
		}
		
		userID := *step.ApproverUserID
		
		// Skip if user is already added
		if existingUsers[userID] {
			continue
		}
		
		existingUsers[userID] = true
		
		recipient := entities.LetterOutgoingRecipient{
			LetterID:  letter.ID,
			UserID:    &userID,
			IsPrimary: false,
			Status:    "pending",
		}
		
		recipients = append(recipients, recipient)
	}

	return recipients
}

// collectDepartmentRecipients gathers all active users from a specific department
func (p *LetterOutgoingRecipientProcessorImpl) collectDepartmentRecipients(
	ctx context.Context,
	letterID uuid.UUID,
	departmentID uuid.UUID,
	existingUsers map[uuid.UUID]bool,
) []entities.LetterOutgoingRecipient {
	var recipients []entities.LetterOutgoingRecipient
	
	// If no department specified, return empty
	if departmentID == uuid.Nil {
		return recipients
	}

	// Fetch all active users in the department
	userDepartmentMappings, err := p.userDeptRepo.ListActiveByDepartmentIDs(ctx, []uuid.UUID{departmentID})
	if err != nil {
		return recipients
	}

	// Add each department user as a recipient
	for _, mapping := range userDepartmentMappings {
		// Skip if user is already added (e.g., from approval workflow)
		if existingUsers[mapping.UserID] {
			continue
		}
		
		existingUsers[mapping.UserID] = true
		
		recipient := entities.LetterOutgoingRecipient{
			LetterID:     letterID,
			UserID:       &mapping.UserID,
			DepartmentID: &mapping.DepartmentID,
			IsPrimary:    false,
			Status:       "pending",
		}
		
		recipients = append(recipients, recipient)
	}

	return recipients
}

// UpdateRecipient updates an existing recipient's information
func (p *LetterOutgoingRecipientProcessorImpl) UpdateRecipient(
	ctx context.Context, 
	recipient *entities.LetterOutgoingRecipient,
) error {
	return p.recipientRepo.Update(ctx, recipient)
}

// RemoveRecipient removes a recipient from a letter
func (p *LetterOutgoingRecipientProcessorImpl) RemoveRecipient(
	ctx context.Context, 
	letterID uuid.UUID, 
	recipientID uuid.UUID,
) error {
	return p.recipientRepo.Delete(ctx, recipientID)
}

// GetRecipientsByLetterID retrieves all recipients for a specific letter
func (p *LetterOutgoingRecipientProcessorImpl) GetRecipientsByLetterID(
	ctx context.Context, 
	letterID uuid.UUID,
) ([]entities.LetterOutgoingRecipient, error) {
	recipientMap, err := p.recipientRepo.ListByLetterIDs(ctx, []uuid.UUID{letterID})
	if err != nil {
		return nil, err
	}
	
	if recipients, ok := recipientMap[letterID]; ok {
		return recipients, nil
	}
	
	return []entities.LetterOutgoingRecipient{}, nil
}