package processor

import (
	"context"

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

	"github.com/google/uuid"
)

type LetterDispositionProcessor interface {
	CreateDispositions(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*contract.ListDispositionsResponse, error)
	GetByLetterID(ctx context.Context, letterID uuid.UUID) ([]entities.LetterIncomingDisposition, error)
	GetEnhancedDispositionsByLetter(ctx context.Context, letterID uuid.UUID) (*contract.ListEnhancedDispositionsResponse, error)
}

type LetterDispositionProcessorImpl struct {
	dispositionRepo          *repository.LetterIncomingDispositionRepository
	dispositionDeptRepo      *repository.LetterIncomingDispositionDepartmentRepository
	dispositionActionSelRepo *repository.LetterDispositionActionSelectionRepository
	dispositionNoteRepo      *repository.DispositionNoteRepository
	discussionRepo           *repository.LetterDiscussionRepository
	dispActionRepo           *repository.DispositionActionRepository
	activity                 *ActivityLogProcessorImpl
}

func NewLetterDispositionProcessor(
	dispositionRepo *repository.LetterIncomingDispositionRepository,
	dispositionDeptRepo *repository.LetterIncomingDispositionDepartmentRepository,
	dispositionActionSelRepo *repository.LetterDispositionActionSelectionRepository,
	dispositionNoteRepo *repository.DispositionNoteRepository,
	discussionRepo *repository.LetterDiscussionRepository,
	dispActionRepo *repository.DispositionActionRepository,
	activity *ActivityLogProcessorImpl,
) *LetterDispositionProcessorImpl {
	return &LetterDispositionProcessorImpl{
		dispositionRepo:          dispositionRepo,
		dispositionDeptRepo:      dispositionDeptRepo,
		dispositionActionSelRepo: dispositionActionSelRepo,
		dispositionNoteRepo:      dispositionNoteRepo,
		discussionRepo:           discussionRepo,
		dispActionRepo:           dispActionRepo,
		activity:                 activity,
	}
}

func (p *LetterDispositionProcessorImpl) CreateDispositions(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*contract.ListDispositionsResponse, error) {
	disposition := &entities.LetterIncomingDisposition{
		LetterID:     req.LetterID,
		DepartmentID: &req.FromDepartment,
		Notes:        req.Notes,
		CreatedBy:    req.CreatedBy,
	}

	if err := p.dispositionRepo.Create(ctx, disposition); err != nil {
		return nil, err
	}

	if err := p.createDispositionDepartments(ctx, disposition.ID, req.LetterID, req.ToDepartmentIDs); err != nil {
		return nil, err
	}

	if len(req.SelectedActions) > 0 {
		if err := p.createActionSelectionsFromRequest(ctx, disposition.ID, req.SelectedActions); err != nil {
			return nil, err
		}
	}

	if p.activity != nil {
		p.activity.LogDispositionCreated(ctx, req.LetterID, req.CreatedBy, len(req.ToDepartmentIDs))
	}

	// Build response
	dispositions := []entities.LetterIncomingDisposition{*disposition}
	response := p.buildDispositionsResponse(dispositions)
	return response, nil
}

func (p *LetterDispositionProcessorImpl) GetByLetterID(ctx context.Context, letterID uuid.UUID) ([]entities.LetterIncomingDisposition, error) {
	return p.dispositionRepo.ListByLetter(ctx, letterID)
}

func (p *LetterDispositionProcessorImpl) GetEnhancedDispositionsByLetter(ctx context.Context, letterID uuid.UUID) (*contract.ListEnhancedDispositionsResponse, error) {
	// Get dispositions
	dispositions, err := p.dispositionRepo.ListByLetter(ctx, letterID)
	if err != nil {
		return nil, err
	}

	// Get discussions
	discussions, err := p.discussionRepo.ListByLetter(ctx, letterID)
	if err != nil {
		return nil, err
	}

	// Build enhanced response
	enhancedDispositions := make([]contract.EnhancedDispositionResponse, 0, len(dispositions))
	for _, disp := range dispositions {
		// Build disposition response using existing structure
		var dept contract.DepartmentResponse
		if disp.Department.ID != uuid.Nil {
			dept = *transformer.DepartmentEntityToContract(&disp.Department)
		}

		// Build departments
		departments := make([]contract.DispositionDepartmentResponse, 0, len(disp.Departments))
		for _, d := range disp.Departments {
			departments = append(departments, contract.DispositionDepartmentResponse{
				ID:           d.ID,
				DepartmentID: d.DepartmentID,
				Department:   transformer.DepartmentEntityToContract(d.Department),
			})
		}

		// Build actions
		actions := make([]contract.DispositionActionSelectionResponse, 0, len(disp.ActionSelections))
		for _, a := range disp.ActionSelections {
			if a.Action != nil {
				actions = append(actions, contract.DispositionActionSelectionResponse{
					ID:       a.ID,
					ActionID: a.ActionID,
					Action: &contract.DispositionActionResponse{
						ID:           a.Action.ID.String(),
						Code:         a.Action.Code,
						Label:        a.Action.Label,
						Description:  a.Action.Description,
						RequiresNote: a.Action.RequiresNote,
					},
				})
			}
		}

		// Build notes
		notes := make([]contract.DispositionNoteResponse, 0, len(disp.DispositionNotes))
		for _, n := range disp.DispositionNotes {
			var userResp *contract.UserResponse
			if n.User != nil {
				userResp = transformer.EntityToContract(n.User)
			}
			notes = append(notes, contract.DispositionNoteResponse{
				ID:        n.ID,
				Note:      n.Note,
				CreatedAt: n.CreatedAt,
				User:      userResp,
			})
		}

		enhancedDispositions = append(enhancedDispositions, contract.EnhancedDispositionResponse{
			ID:               disp.ID,
			LetterID:         disp.LetterID,
			DepartmentID:     disp.DepartmentID,
			Notes:            disp.Notes,
			ReadAt:           disp.ReadAt,
			CreatedBy:        disp.CreatedBy,
			CreatedAt:        disp.CreatedAt,
			UpdatedAt:        disp.UpdatedAt,
			Department:       dept,
			Departments:      departments,
			Actions:          actions,
			DispositionNotes: notes,
		})
	}

	// Get general discussions
	var generalDiscussions []contract.LetterDiscussionResponse
	for _, disc := range discussions {
		generalDiscussions = append(generalDiscussions, *transformer.DiscussionEntityToContract(&disc))
	}

	return &contract.ListEnhancedDispositionsResponse{
		Dispositions: enhancedDispositions,
		Discussions:  generalDiscussions,
	}, nil
}

// Helper methods

func (p *LetterDispositionProcessorImpl) createDispositionDepartments(ctx context.Context, dispositionID, letterID uuid.UUID, departmentIDs []uuid.UUID) error {
	departments := make([]entities.LetterIncomingDispositionDepartment, 0, len(departmentIDs))

	for _, deptID := range departmentIDs {
		departments = append(departments, entities.LetterIncomingDispositionDepartment{
			LetterIncomingDispositionID: dispositionID,
			LetterIncomingID:            letterID,
			DepartmentID:                deptID,
			Status:                      entities.DispositionDepartmentStatusPending,
		})
	}

	if len(departments) > 0 {
		return p.dispositionDeptRepo.CreateBulk(ctx, departments)
	}

	return nil
}

func (p *LetterDispositionProcessorImpl) createActionSelectionsFromRequest(ctx context.Context, dispositionID uuid.UUID, selectedActions []contract.CreateDispositionActionSelection) error {
	selections := make([]entities.LetterDispositionActionSelection, 0, len(selectedActions))

	for _, action := range selectedActions {
		selections = append(selections, entities.LetterDispositionActionSelection{
			DispositionID: dispositionID,
			ActionID:      action.ActionID,
		})
	}

	if len(selections) > 0 {
		return p.dispositionActionSelRepo.CreateBulk(ctx, selections)
	}

	return nil
}

func (p *LetterDispositionProcessorImpl) buildDispositionsResponse(dispositions []entities.LetterIncomingDisposition) *contract.ListDispositionsResponse {
	dispositionResponses := make([]contract.DispositionResponse, 0, len(dispositions))

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

	return &contract.ListDispositionsResponse{
		Dispositions: dispositionResponses,
	}
}

func (p *LetterDispositionProcessorImpl) buildDispositionResponse(disp *entities.LetterIncomingDisposition) *contract.DispositionResponse {
	return &contract.DispositionResponse{
		ID:           disp.ID,
		LetterID:     disp.LetterID,
		DepartmentID: disp.DepartmentID,
		Notes:        disp.Notes,
		ReadAt:       disp.ReadAt,
		CreatedBy:    disp.CreatedBy,
		CreatedAt:    disp.CreatedAt,
		UpdatedAt:    disp.UpdatedAt,
	}
}
