package processor

import (
	"context"
	"time"

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

	"github.com/google/uuid"
)

type LetterDispositionDepartmentProcessor interface {
	GetByLetterIncomingID(ctx context.Context, letterIncomingID uuid.UUID) ([]entities.LetterIncomingDispositionDepartment, error)
	GetDepartmentDispositionStatus(ctx context.Context, letterIncomingID uuid.UUID) (*contract.ListDepartmentDispositionStatusResponse, error)
	UpdateDispositionStatus(ctx context.Context, letterIncomingID uuid.UUID, departmentID uuid.UUID, userID uuid.UUID, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error)
	CheckAndUpdateLetterCompletionStatus(ctx context.Context, letterIncomingID uuid.UUID) error
}

type LetterDispositionDepartmentProcessorImpl struct {
	dispositionDeptRepo *repository.LetterIncomingDispositionDepartmentRepository
	dispositionNoteRepo *repository.DispositionNoteRepository
	letterRepo          *repository.LetterIncomingRepository
}

func NewLetterDispositionDepartmentProcessor(
	dispositionDeptRepo *repository.LetterIncomingDispositionDepartmentRepository,
	dispositionNoteRepo *repository.DispositionNoteRepository,
	letterRepo *repository.LetterIncomingRepository,
) *LetterDispositionDepartmentProcessorImpl {
	return &LetterDispositionDepartmentProcessorImpl{
		dispositionDeptRepo: dispositionDeptRepo,
		dispositionNoteRepo: dispositionNoteRepo,
		letterRepo:          letterRepo,
	}
}

// GetByLetterIncomingID retrieves all disposition departments for a letter
func (p *LetterDispositionDepartmentProcessorImpl) GetByLetterIncomingID(ctx context.Context, letterIncomingID uuid.UUID) ([]entities.LetterIncomingDispositionDepartment, error) {
	return p.dispositionDeptRepo.GetByLetterIncomingID(ctx, letterIncomingID)
}

// GetDepartmentDispositionStatus retrieves disposition status for a specific letter
func (p *LetterDispositionDepartmentProcessorImpl) GetDepartmentDispositionStatus(ctx context.Context, letterIncomingID uuid.UUID) (*contract.ListDepartmentDispositionStatusResponse, error) {
	dispositions, err := p.dispositionDeptRepo.GetByLetterIncomingID(ctx, letterIncomingID)
	if err != nil {
		return nil, err
	}

	response := p.buildDispositionStatusResponse(dispositions)
	return response, nil
}

func (p *LetterDispositionDepartmentProcessorImpl) UpdateDispositionStatus(ctx context.Context, letterIncomingID uuid.UUID, departmentID uuid.UUID, userID uuid.UUID, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error) {
	dispDept, err := p.dispositionDeptRepo.GetByDispositionAndDepartment(ctx, letterIncomingID, departmentID)
	if err != nil {
		return nil, err
	}

	now := time.Now()
	var dispositionStatus entities.LetterIncomingDispositionDepartmentStatus
	var readAt, completedAt *time.Time

	switch req.Status {
	case "completed":
		dispositionStatus = entities.DispositionDepartmentStatusCompleted
		completedAt = &now
		readAt = &now // Mark as read when completing
	case "read":
		dispositionStatus = entities.DispositionDepartmentStatusRead
		readAt = &now
	case "dispositioned":
		dispositionStatus = entities.DispositionDepartmentStatusDispositioned
	default:
		dispositionStatus = entities.DispositionDepartmentStatusPending
	}

	// Extract notes for the update
	notes := ""
	if req.Notes != nil && *req.Notes != "" {
		notes = *req.Notes
	}

	if err := p.dispositionDeptRepo.UpdateStatus(ctx, dispDept.ID, dispositionStatus, notes, readAt, completedAt); err != nil {
		return nil, err
	}

	// Check and update letter completion status
	if err := p.CheckAndUpdateLetterCompletionStatus(ctx, letterIncomingID); err != nil {
		// Log error but don't fail the status update
	}

	// Get updated record for response
	updatedDispDept, err := p.dispositionDeptRepo.GetByID(ctx, dispDept.ID)
	if err != nil {
		return nil, err
	}

	return p.buildSingleDispositionStatusResponse(updatedDispDept), nil
}

// CheckAndUpdateLetterCompletionStatus checks if all dispositions are completed and updates letter status
func (p *LetterDispositionDepartmentProcessorImpl) CheckAndUpdateLetterCompletionStatus(ctx context.Context, letterIncomingID uuid.UUID) error {
	// Get all disposition departments for this letter
	dispositions, err := p.dispositionDeptRepo.GetByLetterIncomingID(ctx, letterIncomingID)
	if err != nil {
		return err
	}

	// Check if all dispositions are completed
	allCompleted := true
	for _, disp := range dispositions {
		if disp.Status == entities.DispositionDepartmentStatusPending {
			allCompleted = false
			break
		}
	}

	// If all dispositions are completed, update the letter status to completed
	if allCompleted && len(dispositions) > 0 {
		letter, err := p.letterRepo.GetByID(ctx, letterIncomingID)
		if err != nil {
			return err
		}

		letter.Status = "completed"
		if err := p.letterRepo.Update(ctx, letter); err != nil {
			return err
		}
	}

	return nil
}

// Helper methods

func (p *LetterDispositionDepartmentProcessorImpl) buildDispositionStatusResponse(dispositions []entities.LetterIncomingDispositionDepartment) *contract.ListDepartmentDispositionStatusResponse {
	var response []contract.DepartmentDispositionStatusResponse

	for _, disp := range dispositions {
		response = append(response, *p.buildSingleDispositionStatusResponse(&disp))
	}

	return &contract.ListDepartmentDispositionStatusResponse{
		Dispositions: response,
		Pagination: contract.PaginationResponse{
			TotalCount: len(response),
			Page:       1,
			Limit:      len(response),
			TotalPages: 1,
		},
	}
}

func (p *LetterDispositionDepartmentProcessorImpl) buildSingleDispositionStatusResponse(dispDept *entities.LetterIncomingDispositionDepartment) *contract.DepartmentDispositionStatusResponse {
	letterResp := transformer.LetterIncomingEntityToContract(dispDept.LetterIncoming)

	var fromDept *contract.DepartmentResponse
	if dispDept.LetterIncomingDisposition != nil && dispDept.LetterIncomingDisposition.DepartmentID != nil {
		fromDept = transformer.DepartmentEntityToContract(&dispDept.LetterIncomingDisposition.Department)
	}

	return &contract.DepartmentDispositionStatusResponse{
		ID:               dispDept.ID,
		LetterID:         dispDept.LetterIncomingID,
		Letter:           letterResp,
		FromDepartmentID: dispDept.LetterIncomingDisposition.DepartmentID,
		FromDepartment:   fromDept,
		ToDepartmentID:   dispDept.DepartmentID,
		ToDepartment:     transformer.DepartmentEntityToContract(dispDept.Department),
		Status:           string(dispDept.Status),
		Notes:            dispDept.LetterIncomingDisposition.Notes,
		ReadAt:           dispDept.ReadAt,
		CompletedAt:      dispDept.CompletedAt,
		CreatedAt:        dispDept.CreatedAt,
		UpdatedAt:        dispDept.UpdatedAt,
	}
}
