package repository

import (
	"context"
	"time"

	"eslogad-be/internal/entities"

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

type LetterIncomingRepository struct{ db *gorm.DB }

func NewLetterIncomingRepository(db *gorm.DB) *LetterIncomingRepository {
	return &LetterIncomingRepository{db: db}
}

func (r *LetterIncomingRepository) Create(ctx context.Context, e *entities.LetterIncoming) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(e).Error
}
func (r *LetterIncomingRepository) Get(ctx context.Context, id uuid.UUID) (*entities.LetterIncoming, error) {
	db := DBFromContext(ctx, r.db)
	var e entities.LetterIncoming
	if err := db.WithContext(ctx).First(&e, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return &e, nil
}

func (r *LetterIncomingRepository) GetByID(ctx context.Context, id uuid.UUID) (*entities.LetterIncoming, error) {
	return r.Get(ctx, id)
}

func (r *LetterIncomingRepository) Update(ctx context.Context, e *entities.LetterIncoming) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Model(&entities.LetterIncoming{}).Where("id = ? AND deleted_at IS NULL", e.ID).Updates(e).Error
}

func (r *LetterIncomingRepository) SoftDelete(ctx context.Context, id uuid.UUID) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Exec("UPDATE letters_incoming SET deleted_at = CURRENT_TIMESTAMP WHERE id = ? AND deleted_at IS NULL", id).Error
}

func (r *LetterIncomingRepository) BulkArchive(ctx context.Context, letterIDs []uuid.UUID) (int64, error) {
	db := DBFromContext(ctx, r.db)
	// For incoming letters, we archive the recipients, not the letter itself
	// The letter status remains as is (new, in_progress, or completed)
	result := db.WithContext(ctx).
		Model(&entities.LetterIncomingRecipient{}).
		Where("letter_id IN ?", letterIDs).
		Update("is_archived", true)
	return result.RowsAffected, result.Error
}

// BulkArchiveForUser archives letters for a specific user only
func (r *LetterIncomingRepository) BulkArchiveForUser(ctx context.Context, letterIDs []uuid.UUID, userID uuid.UUID) (int64, error) {
	db := DBFromContext(ctx, r.db)
	// Archive only the recipient records for the specific user
	// Note: letter_incoming_recipients uses recipient_user_id column
	result := db.WithContext(ctx).
		Model(&entities.LetterIncomingRecipient{}).
		Where("letter_id IN ? AND recipient_user_id = ?", letterIDs, userID).
		Update("is_archived", true)
	return result.RowsAffected, result.Error
}

type ListIncomingLettersFilter struct {
	Status          *string
	Query           *string
	DepartmentID    *uuid.UUID
	UserID          *uuid.UUID
	IsRead          *bool
	PriorityIDs     []uuid.UUID
	IsDispositioned *bool
	IsArchived      *bool
}

func (r *LetterIncomingRepository) List(ctx context.Context, filter ListIncomingLettersFilter, limit, offset int) ([]entities.LetterIncoming, int64, error) {
	db := DBFromContext(ctx, r.db)
	query := db.WithContext(ctx).Model(&entities.LetterIncoming{}).Where("deleted_at IS NULL")

	joinedRecipients := false
	needsGroupBy := false

	if filter.DepartmentID != nil {
		query = query.Joins("JOIN letter_incoming_recipients ON letter_incoming_recipients.letter_id = letters_incoming.id").
			Where("letter_incoming_recipients.recipient_department_id = ?", *filter.DepartmentID)
		joinedRecipients = true
		needsGroupBy = true
	}

	if filter.UserID != nil && filter.IsRead != nil {
		if !joinedRecipients {
			query = query.Joins("JOIN letter_incoming_recipients ON letter_incoming_recipients.letter_id = letters_incoming.id")
			joinedRecipients = true
			needsGroupBy = true
		}
		query = query.Where("letter_incoming_recipients.recipient_user_id = ?", *filter.UserID)

		if *filter.IsRead {
			query = query.Where("letter_incoming_recipients.read_at IS NOT NULL")
		} else {
			query = query.Where("letter_incoming_recipients.read_at IS NULL")
		}
	}

	if filter.DepartmentID != nil && filter.IsDispositioned != nil {
		query = query.Joins("LEFT JOIN letter_incoming_dispositions_department lidd ON lidd.letter_incoming_id = letters_incoming.id AND lidd.department_id = ?", *filter.DepartmentID)

		if *filter.IsDispositioned {
			query = query.Where("lidd.id IS NOT NULL AND lidd.status != 'pending'")
		} else {
			query = query.Where("lidd.id IS NULL OR lidd.status = 'pending'")
		}
	}

	if len(filter.PriorityIDs) > 0 {
		query = query.Where("letters_incoming.priority_id IN ?", filter.PriorityIDs)
	}

	if filter.IsArchived != nil {
		if *filter.IsArchived {
			query = query.Where("letter_incoming_recipients.is_archived = ?", true)
		} else {
			query = query.Where("letter_incoming_recipients.is_archived = ? OR letter_incoming_recipients.is_archived IS NULL", false)
		}
	}

	if filter.Status != nil {
		query = query.Where("letters_incoming.status = ?", *filter.Status)
	}

	if filter.Query != nil {
		q := "%" + *filter.Query + "%"
		query = query.Where("letters_incoming.subject ILIKE ? OR letters_incoming.reference_number ILIKE ?", q, q)
	}

	if needsGroupBy {
		query = query.Group("letters_incoming.id")
	}

	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// For the actual data fetch, we need to select all columns
	var list []entities.LetterIncoming
	dataQuery := db.WithContext(ctx).Model(&entities.LetterIncoming{}).Where("deleted_at IS NULL")

	if filter.DepartmentID != nil {
		dataQuery = dataQuery.Joins("JOIN letter_incoming_recipients ON letter_incoming_recipients.letter_id = letters_incoming.id").
			Where("letter_incoming_recipients.recipient_department_id = ?", *filter.DepartmentID)
	}

	if filter.UserID != nil && filter.IsRead != nil {
		if filter.DepartmentID == nil {
			dataQuery = dataQuery.Joins("JOIN letter_incoming_recipients ON letter_incoming_recipients.letter_id = letters_incoming.id")
		}
		dataQuery = dataQuery.Where("letter_incoming_recipients.recipient_user_id = ?", *filter.UserID)

		if *filter.IsRead {
			dataQuery = dataQuery.Where("letter_incoming_recipients.read_at IS NOT NULL")
		} else {
			dataQuery = dataQuery.Where("letter_incoming_recipients.read_at IS NULL")
		}
	}

	if filter.DepartmentID != nil && filter.IsDispositioned != nil {
		dataQuery = dataQuery.Joins("LEFT JOIN letter_incoming_dispositions_department lidd ON lidd.letter_incoming_id = letters_incoming.id AND lidd.department_id = ?", *filter.DepartmentID)

		if *filter.IsDispositioned {
			dataQuery = dataQuery.Where("lidd.id IS NOT NULL AND lidd.status != 'pending'")
		} else {
			dataQuery = dataQuery.Where("lidd.id IS NULL OR lidd.status = 'pending'")
		}
	}

	if len(filter.PriorityIDs) > 0 {
		dataQuery = dataQuery.Where("letters_incoming.priority_id IN ?", filter.PriorityIDs)
	}

	// Apply is_archived filter based on recipient's is_archived field
	if filter.IsArchived != nil {
		if *filter.IsArchived {
			dataQuery = dataQuery.Where("letter_incoming_recipients.is_archived = ?", true)
		} else {
			dataQuery = dataQuery.Where("letter_incoming_recipients.is_archived = ? OR letter_incoming_recipients.is_archived IS NULL", false)
		}
	}

	if filter.Status != nil {
		dataQuery = dataQuery.Where("letters_incoming.status = ?", *filter.Status)
	}

	if filter.Query != nil {
		q := "%" + *filter.Query + "%"
		dataQuery = dataQuery.Where("letters_incoming.subject ILIKE ? OR letters_incoming.reference_number ILIKE ?", q, q)
	}

	if needsGroupBy {
		dataQuery = dataQuery.Group("letters_incoming.id, letters_incoming.letter_number, letters_incoming.reference_number, letters_incoming.subject, letters_incoming.description, letters_incoming.priority_id, letters_incoming.sender_institution_id, letters_incoming.received_date, letters_incoming.due_date, letters_incoming.status, letters_incoming.created_by, letters_incoming.created_at, letters_incoming.updated_at, letters_incoming.deleted_at")
	}

	if err := dataQuery.Order("letters_incoming.created_at DESC").Limit(limit).Offset(offset).Find(&list).Error; err != nil {
		return nil, 0, err
	}
	return list, total, nil
}

type LetterIncomingAttachmentRepository struct{ db *gorm.DB }

func NewLetterIncomingAttachmentRepository(db *gorm.DB) *LetterIncomingAttachmentRepository {
	return &LetterIncomingAttachmentRepository{db: db}
}

func (r *LetterIncomingAttachmentRepository) CreateBulk(ctx context.Context, list []entities.LetterIncomingAttachment) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(&list).Error
}
func (r *LetterIncomingAttachmentRepository) ListByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterIncomingAttachment, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterIncomingAttachment
	if err := db.WithContext(ctx).Where("letter_id = ?", letterID).Order("uploaded_at ASC").Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

func (r *LetterIncomingAttachmentRepository) ListByLetterIDs(ctx context.Context, letterIDs []uuid.UUID) (map[uuid.UUID][]entities.LetterIncomingAttachment, error) {
	if len(letterIDs) == 0 {
		return make(map[uuid.UUID][]entities.LetterIncomingAttachment), nil
	}

	db := DBFromContext(ctx, r.db)
	var attachments []entities.LetterIncomingAttachment
	if err := db.WithContext(ctx).Where("letter_id IN ?", letterIDs).Order("uploaded_at ASC").Find(&attachments).Error; err != nil {
		return nil, err
	}

	// Group attachments by letter ID
	result := make(map[uuid.UUID][]entities.LetterIncomingAttachment)
	for _, att := range attachments {
		result[att.LetterID] = append(result[att.LetterID], att)
	}

	return result, nil
}

type LetterIncomingActivityLogRepository struct{ db *gorm.DB }

func NewLetterIncomingActivityLogRepository(db *gorm.DB) *LetterIncomingActivityLogRepository {
	return &LetterIncomingActivityLogRepository{db: db}
}

func (r *LetterIncomingActivityLogRepository) Create(ctx context.Context, e *entities.LetterIncomingActivityLog) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(e).Error
}

func (r *LetterIncomingActivityLogRepository) ListByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterIncomingActivityLog, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterIncomingActivityLog
	if err := db.WithContext(ctx).Where("letter_id = ?", letterID).Order("occurred_at ASC").Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

type LetterIncomingDispositionRepository struct{ db *gorm.DB }

func NewLetterIncomingDispositionRepository(db *gorm.DB) *LetterIncomingDispositionRepository {
	return &LetterIncomingDispositionRepository{db: db}
}
func (r *LetterIncomingDispositionRepository) Create(ctx context.Context, e *entities.LetterIncomingDisposition) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(e).Error
}

func (r *LetterIncomingDispositionRepository) GetByID(ctx context.Context, id uuid.UUID) (*entities.LetterIncomingDisposition, error) {
	db := DBFromContext(ctx, r.db)
	var e entities.LetterIncomingDisposition
	if err := db.WithContext(ctx).
		Preload("Department").
		Preload("Departments.Department").
		Preload("ActionSelections.Action").
		Preload("DispositionNotes.User").
		First(&e, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return &e, nil
}

func (r *LetterIncomingDispositionRepository) ListByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterIncomingDisposition, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterIncomingDisposition
	if err := db.WithContext(ctx).
		Where("letter_id = ?", letterID).
		Preload("Department").
		Preload("Departments.Department").
		Preload("ActionSelections.Action").
		Preload("DispositionNotes.User").
		Order("created_at ASC").
		Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

func (r *LetterIncomingDispositionRepository) GetByLetterIncomingID(ctx context.Context, letterIncomingID uuid.UUID) ([]entities.LetterIncomingDisposition, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterIncomingDisposition
	if err := db.WithContext(ctx).
		Where("letter_id = ?", letterIncomingID).
		Preload("Department").
		Preload("Departments.Department").
		Order("created_at ASC").
		Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

type LetterIncomingDispositionDepartmentRepository struct{ db *gorm.DB }

func NewLetterIncomingDispositionDepartmentRepository(db *gorm.DB) *LetterIncomingDispositionDepartmentRepository {
	return &LetterIncomingDispositionDepartmentRepository{db: db}
}

func (r *LetterIncomingDispositionDepartmentRepository) DB(ctx context.Context) *gorm.DB {
	return DBFromContext(ctx, r.db)
}

func (r *LetterIncomingDispositionDepartmentRepository) Create(ctx context.Context, e *entities.LetterIncomingDispositionDepartment) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(e).Error
}

func (r *LetterIncomingDispositionDepartmentRepository) CreateBulk(ctx context.Context, list []entities.LetterIncomingDispositionDepartment) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(&list).Error
}

func (r *LetterIncomingDispositionDepartmentRepository) Update(ctx context.Context, e *entities.LetterIncomingDispositionDepartment) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Save(e).Error
}

func (r *LetterIncomingDispositionDepartmentRepository) GetByID(ctx context.Context, id uuid.UUID) (*entities.LetterIncomingDispositionDepartment, error) {
	db := DBFromContext(ctx, r.db)
	var e entities.LetterIncomingDispositionDepartment
	if err := db.WithContext(ctx).
		Preload("Department").
		Preload("LetterIncoming").
		Preload("LetterIncomingDisposition").
		First(&e, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return &e, nil
}

func (r *LetterIncomingDispositionDepartmentRepository) GetByDispositionAndDepartment(ctx context.Context, letterIncomingID, departmentID uuid.UUID) (*entities.LetterIncomingDispositionDepartment, error) {
	db := DBFromContext(ctx, r.db)
	var e entities.LetterIncomingDispositionDepartment
	if err := db.WithContext(ctx).
		Where("letter_incoming_id = ? AND department_id = ?", letterIncomingID, departmentID).
		Preload("Department").
		Preload("LetterIncoming").
		Preload("LetterIncomingDisposition").
		First(&e).Error; err != nil {
		return nil, err
	}
	return &e, nil
}

func (r *LetterIncomingDispositionDepartmentRepository) GetByLetterAndDepartment(ctx context.Context, letterID, departmentID uuid.UUID) ([]entities.LetterIncomingDispositionDepartment, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterIncomingDispositionDepartment
	if err := db.WithContext(ctx).
		Where("letter_incoming_id = ? AND department_id = ?", letterID, departmentID).
		Preload("Department").
		Preload("LetterIncoming").
		Preload("LetterIncomingDisposition").
		Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

func (r *LetterIncomingDispositionDepartmentRepository) ListByDepartmentWithPagination(ctx context.Context, departmentID uuid.UUID, status *string, offset, limit int) ([]entities.LetterIncomingDispositionDepartment, int64, error) {
	db := DBFromContext(ctx, r.db)
	query := db.WithContext(ctx).Where("department_id = ?", departmentID)

	if status != nil && *status != "" {
		query = query.Where("status = ?", *status)
	}

	var total int64
	if err := query.Model(&entities.LetterIncomingDispositionDepartment{}).Count(&total).Error; err != nil {
		return nil, 0, err
	}

	var list []entities.LetterIncomingDispositionDepartment
	if err := query.
		Preload("Department").
		Preload("LetterIncoming").
		Preload("LetterIncomingDisposition.Department").
		Offset(offset).
		Limit(limit).
		Order("created_at DESC").
		Find(&list).Error; err != nil {
		return nil, 0, err
	}

	return list, total, nil
}

func (r *LetterIncomingDispositionDepartmentRepository) GetByLetterIncomingID(ctx context.Context, letterIncomingID uuid.UUID) ([]entities.LetterIncomingDispositionDepartment, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterIncomingDispositionDepartment
	if err := db.WithContext(ctx).
		Where("letter_incoming_id = ?", letterIncomingID).
		Preload("Department").
		Preload("LetterIncoming").
		Preload("LetterIncomingDisposition.Department").
		Order("created_at DESC").
		Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

func (r *LetterIncomingDispositionDepartmentRepository) UpdateStatus(ctx context.Context, id uuid.UUID, status entities.LetterIncomingDispositionDepartmentStatus, notes string, readAt, completedAt *time.Time) error {
	db := DBFromContext(ctx, r.db)
	updates := map[string]interface{}{
		"status": status,
	}
	if readAt != nil {
		updates["read_at"] = readAt
	}
	if completedAt != nil {
		updates["completed_at"] = completedAt
	}

	if notes != "" {
		updates["notes"] = notes
	}
	return db.WithContext(ctx).Model(&entities.LetterIncomingDispositionDepartment{}).
		Where("id = ?", id).
		Updates(updates).Error
}

func (r *LetterIncomingDispositionDepartmentRepository) ListByDisposition(ctx context.Context, dispositionID uuid.UUID) ([]entities.LetterIncomingDispositionDepartment, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterIncomingDispositionDepartment
	if err := db.WithContext(ctx).Where("letter_incoming_disposition_id = ?", dispositionID).Order("created_at ASC").Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

func (r *LetterIncomingDispositionDepartmentRepository) ListByDispositions(ctx context.Context, dispositionIDs []uuid.UUID) ([]entities.LetterIncomingDispositionDepartment, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterIncomingDispositionDepartment
	if len(dispositionIDs) == 0 {
		return list, nil
	}
	if err := db.WithContext(ctx).Where("letter_incoming_disposition_id IN ?", dispositionIDs).Order("letter_incoming_disposition_id, created_at ASC").Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

type DispositionNoteRepository struct{ db *gorm.DB }

func NewDispositionNoteRepository(db *gorm.DB) *DispositionNoteRepository {
	return &DispositionNoteRepository{db: db}
}
func (r *DispositionNoteRepository) Create(ctx context.Context, e *entities.DispositionNote) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(e).Error
}

func (r *DispositionNoteRepository) ListByDisposition(ctx context.Context, dispositionID uuid.UUID) ([]entities.DispositionNote, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.DispositionNote
	if err := db.WithContext(ctx).Where("disposition_id = ?", dispositionID).Order("created_at ASC").Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

func (r *DispositionNoteRepository) ListByDispositions(ctx context.Context, dispositionIDs []uuid.UUID) ([]entities.DispositionNote, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.DispositionNote
	if len(dispositionIDs) == 0 {
		return list, nil
	}
	if err := db.WithContext(ctx).Where("disposition_id IN ?", dispositionIDs).Order("disposition_id, created_at ASC").Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

type LetterDispositionActionSelectionRepository struct{ db *gorm.DB }

func NewLetterDispositionActionSelectionRepository(db *gorm.DB) *LetterDispositionActionSelectionRepository {
	return &LetterDispositionActionSelectionRepository{db: db}
}
func (r *LetterDispositionActionSelectionRepository) CreateBulk(ctx context.Context, list []entities.LetterDispositionActionSelection) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(&list).Error
}
func (r *LetterDispositionActionSelectionRepository) ListByDisposition(ctx context.Context, dispositionID uuid.UUID) ([]entities.LetterDispositionActionSelection, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterDispositionActionSelection
	if err := db.WithContext(ctx).Where("disposition_id = ?", dispositionID).Order("created_at ASC").Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

func (r *LetterDispositionActionSelectionRepository) ListByDispositions(ctx context.Context, dispositionIDs []uuid.UUID) ([]entities.LetterDispositionActionSelection, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterDispositionActionSelection
	if len(dispositionIDs) == 0 {
		return list, nil
	}
	if err := db.WithContext(ctx).Where("disposition_id IN ?", dispositionIDs).Order("disposition_id, created_at ASC").Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

type LetterDiscussionRepository struct{ db *gorm.DB }

func NewLetterDiscussionRepository(db *gorm.DB) *LetterDiscussionRepository {
	return &LetterDiscussionRepository{db: db}
}
func (r *LetterDiscussionRepository) Create(ctx context.Context, e *entities.LetterDiscussion) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(e).Error
}
func (r *LetterDiscussionRepository) Get(ctx context.Context, id uuid.UUID) (*entities.LetterDiscussion, error) {
	db := DBFromContext(ctx, r.db)
	var e entities.LetterDiscussion
	if err := db.WithContext(ctx).First(&e, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return &e, nil
}
func (r *LetterDiscussionRepository) Update(ctx context.Context, e *entities.LetterDiscussion) error {
	db := DBFromContext(ctx, r.db)
	// ensure edited_at is set when updating
	if e.EditedAt == nil {
		now := time.Now()
		e.EditedAt = &now
	}
	return db.WithContext(ctx).Model(&entities.LetterDiscussion{}).
		Where("id = ?", e.ID).
		Updates(map[string]interface{}{"message": e.Message, "mentions": e.Mentions, "edited_at": e.EditedAt}).Error
}

func (r *LetterDiscussionRepository) ListByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterDiscussion, error) {
	db := DBFromContext(ctx, r.db)
	var list []entities.LetterDiscussion
	if err := db.WithContext(ctx).
		Where("letter_id = ?", letterID).
		Preload("User.Profile").
		Order("created_at ASC").
		Find(&list).Error; err != nil {
		return nil, err
	}
	return list, nil
}

func (r *LetterDiscussionRepository) GetUsersByIDs(ctx context.Context, userIDs []uuid.UUID) ([]entities.User, error) {
	if len(userIDs) == 0 {
		return []entities.User{}, nil
	}

	db := DBFromContext(ctx, r.db)
	var users []entities.User
	if err := db.WithContext(ctx).
		Where("id IN ?", userIDs).
		Preload("Profile").
		Find(&users).Error; err != nil {
		return nil, err
	}
	return users, nil
}

// recipients

type LetterIncomingRecipientRepository struct{ db *gorm.DB }

func NewLetterIncomingRecipientRepository(db *gorm.DB) *LetterIncomingRecipientRepository {
	return &LetterIncomingRecipientRepository{db: db}
}
func (r *LetterIncomingRecipientRepository) DB(ctx context.Context) *gorm.DB {
	return DBFromContext(ctx, r.db)
}

func (r *LetterIncomingRecipientRepository) CreateBulk(ctx context.Context, recs []entities.LetterIncomingRecipient) error {
	db := DBFromContext(ctx, r.db)
	return db.WithContext(ctx).Create(&recs).Error
}

func (r *LetterIncomingRecipientRepository) Create(ctx context.Context, recipient *entities.LetterIncomingRecipient) error {
	return r.DB(ctx).Create(recipient).Error
}

func (r *LetterIncomingRecipientRepository) Update(ctx context.Context, recipient *entities.LetterIncomingRecipient) error {
	return r.DB(ctx).Save(recipient).Error
}

func (r *LetterIncomingRecipientRepository) GetByID(ctx context.Context, id uuid.UUID) (*entities.LetterIncomingRecipient, error) {
	var recipient entities.LetterIncomingRecipient
	if err := r.DB(ctx).Where("id = ?", id).First(&recipient).Error; err != nil {
		return nil, err
	}
	return &recipient, nil
}

func (r *LetterIncomingRecipientRepository) GetByLetterAndDepartment(ctx context.Context, letterID uuid.UUID, departmentID uuid.UUID) (*entities.LetterIncomingRecipient, error) {
	var recipient entities.LetterIncomingRecipient
	if err := r.DB(ctx).Where("letter_id = ? AND recipient_department_id = ?", letterID, departmentID).First(&recipient).Error; err != nil {
		return nil, err
	}
	return &recipient, nil
}

func (r *LetterIncomingRecipientRepository) GetByLetterAndUser(ctx context.Context, letterID uuid.UUID, userID uuid.UUID) (*entities.LetterIncomingRecipient, error) {
	var recipient entities.LetterIncomingRecipient
	if err := r.DB(ctx).Where("letter_id = ? AND recipient_user_id = ?", letterID, userID).First(&recipient).Error; err != nil {
		return nil, err
	}
	return &recipient, nil
}

func (r *LetterIncomingRecipientRepository) ListByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterIncomingRecipient, error) {
	var recipients []entities.LetterIncomingRecipient
	if err := r.DB(ctx).Where("letter_id = ?", letterID).Find(&recipients).Error; err != nil {
		return nil, err
	}
	return recipients, nil
}

func (r *LetterIncomingRecipientRepository) ListByDepartment(ctx context.Context, departmentID uuid.UUID) ([]entities.LetterIncomingRecipient, error) {
	var recipients []entities.LetterIncomingRecipient
	if err := r.DB(ctx).Where("recipient_department_id = ?", departmentID).Find(&recipients).Error; err != nil {
		return nil, err
	}
	return recipients, nil
}

func (r *LetterIncomingRecipientRepository) GetLetterIDsByDepartment(ctx context.Context, departmentID uuid.UUID) ([]uuid.UUID, error) {
	db := DBFromContext(ctx, r.db)
	var letterIDs []uuid.UUID
	if err := db.WithContext(ctx).
		Model(&entities.LetterIncomingRecipient{}).
		Where("recipient_department_id = ?", departmentID).
		Distinct("letter_id").
		Pluck("letter_id", &letterIDs).Error; err != nil {
		return nil, err
	}
	return letterIDs, nil
}

func (r *LetterIncomingRecipientRepository) CountReadByLetter(ctx context.Context, letterID uuid.UUID) (int, error) {
	db := DBFromContext(ctx, r.db)
	var count int64
	if err := db.WithContext(ctx).
		Model(&entities.LetterIncomingRecipient{}).
		Where("letter_id = ? AND read_at IS NOT NULL", letterID).
		Count(&count).Error; err != nil {
		return 0, err
	}
	return int(count), nil
}

func (r *LetterIncomingRecipientRepository) CountUnreadByUser(ctx context.Context, userID uuid.UUID) (int, error) {
	db := DBFromContext(ctx, r.db)
	var count int64
	if err := db.WithContext(ctx).
		Model(&entities.LetterIncomingRecipient{}).
		Where("recipient_user_id = ? AND read_at IS NULL", userID).
		Count(&count).Error; err != nil {
		return 0, err
	}
	return int(count), nil
}

func (r *LetterIncomingRecipientRepository) MarkAsRead(ctx context.Context, letterID, userID uuid.UUID) error {
	db := DBFromContext(ctx, r.db)
	now := time.Now()
	return db.WithContext(ctx).
		Model(&entities.LetterIncomingRecipient{}).
		Where("letter_id = ? AND recipient_user_id = ?", letterID, userID).
		Update("read_at", now).Error
}

func (r *LetterIncomingRecipientRepository) GetByLetterIDsAndUser(ctx context.Context, letterIDs []uuid.UUID, userID uuid.UUID) (map[uuid.UUID]*entities.LetterIncomingRecipient, error) {
	if len(letterIDs) == 0 {
		return make(map[uuid.UUID]*entities.LetterIncomingRecipient), nil
	}

	db := DBFromContext(ctx, r.db)
	var recipients []entities.LetterIncomingRecipient
	if err := db.WithContext(ctx).
		Where("letter_id IN ? AND recipient_user_id = ?", letterIDs, userID).
		Find(&recipients).Error; err != nil {
		return nil, err
	}

	result := make(map[uuid.UUID]*entities.LetterIncomingRecipient)
	for i := range recipients {
		result[recipients[i].LetterID] = &recipients[i]
	}

	return result, nil
}

func (r *LetterIncomingRecipientRepository) HasDepartmentAccess(ctx context.Context, letterID uuid.UUID, departmentID uuid.UUID) (bool, error) {
	db := DBFromContext(ctx, r.db)
	var count int64
	if err := db.WithContext(ctx).
		Model(&entities.LetterIncomingRecipient{}).
		Where("letter_id = ? AND recipient_department_id = ?", letterID, departmentID).
		Count(&count).Error; err != nil {
		return false, err
	}
	return count > 0, nil
}
