package transformer

import (
	"eslogad-be/internal/contract"
	"eslogad-be/internal/entities"

	"github.com/google/uuid"
)

func LetterEntityToContract(e *entities.LetterIncoming, attachments []entities.LetterIncomingAttachment, refs ...interface{}) *contract.IncomingLetterResponse {
	resp := &contract.IncomingLetterResponse{
		ID:              e.ID,
		LetterNumber:    e.LetterNumber,
		ReferenceNumber: e.ReferenceNumber,
		Subject:         e.Subject,
		Description:     e.Description,
		ReceivedDate:    e.ReceivedDate,
		DueDate:         e.DueDate,
		Status:          string(e.Status),
		CreatedBy:       e.CreatedBy,
		CreatedAt:       e.CreatedAt,
		UpdatedAt:       e.UpdatedAt,
		Attachments:     make([]contract.IncomingLetterAttachmentResponse, 0, len(attachments)),
	}

	// optional refs: allow passing already-fetched related objects
	// expected ordering (if provided): *entities.Priority, *entities.Institution
	for _, r := range refs {
		switch v := r.(type) {
		case *entities.Priority:
			if v != nil {
				resp.Priority = &contract.PriorityResponse{
					ID:        v.ID.String(),
					Name:      v.Name,
					Level:     v.Level,
					CreatedAt: v.CreatedAt,
					UpdatedAt: v.UpdatedAt,
				}
			}
		case *entities.Institution:
			if v != nil {
				resp.SenderInstitution = &contract.InstitutionResponse{
					ID:            v.ID.String(),
					Name:          v.Name,
					Type:          string(v.Type),
					Address:       v.Address,
					ContactPerson: v.ContactPerson,
					Phone:         v.Phone,
					Email:         v.Email,
					CreatedAt:     v.CreatedAt,
					UpdatedAt:     v.UpdatedAt,
				}
			}
		}
	}
	for _, a := range attachments {
		resp.Attachments = append(resp.Attachments, contract.IncomingLetterAttachmentResponse{
			ID:         a.ID,
			FileURL:    a.FileURL,
			FileName:   a.FileName,
			FileType:   a.FileType,
			UploadedAt: a.UploadedAt,
		})
	}
	return resp
}

func DispositionsToContract(list []entities.LetterIncomingDisposition) []contract.DispositionResponse {
	out := make([]contract.DispositionResponse, 0, len(list))
	for _, d := range list {
		out = append(out, DispoToContract(d))
	}
	return out
}

func DispoToContract(d entities.LetterIncomingDisposition) contract.DispositionResponse {
	return contract.DispositionResponse{
		ID:           d.ID,
		LetterID:     d.LetterID,
		DepartmentID: d.DepartmentID,
		Notes:        d.Notes,
		ReadAt:       d.ReadAt,
		CreatedBy:    d.CreatedBy,
		CreatedAt:    d.CreatedAt,
		UpdatedAt:    d.UpdatedAt,
	}
}

func EnhancedDispositionsToContract(list []entities.LetterIncomingDisposition) []contract.EnhancedDispositionResponse {
	out := make([]contract.EnhancedDispositionResponse, 0, len(list))
	for _, d := range list {
		resp := contract.EnhancedDispositionResponse{
			ID:               d.ID,
			LetterID:         d.LetterID,
			DepartmentID:     d.DepartmentID,
			Notes:            d.Notes,
			ReadAt:           d.ReadAt,
			CreatedBy:        d.CreatedBy,
			CreatedAt:        d.CreatedAt,
			UpdatedAt:        d.UpdatedAt,
			Departments:      []contract.DispositionDepartmentResponse{},
			Actions:          []contract.DispositionActionSelectionResponse{},
			DispositionNotes: []contract.DispositionNoteResponse{},
		}
		out = append(out, resp)
	}
	return out
}

func DispositionDepartmentsToContract(list []entities.LetterIncomingDispositionDepartment) []contract.DispositionDepartmentResponse {
	out := make([]contract.DispositionDepartmentResponse, 0, len(list))
	for _, d := range list {
		resp := contract.DispositionDepartmentResponse{
			ID:           d.ID,
			DepartmentID: d.DepartmentID,
			CreatedAt:    d.CreatedAt,
		}
		out = append(out, resp)
	}
	return out
}

func DispositionDepartmentsWithDetailsToContract(list []entities.LetterIncomingDispositionDepartment) []contract.DispositionDepartmentResponse {
	out := make([]contract.DispositionDepartmentResponse, 0, len(list))
	for _, d := range list {
		resp := contract.DispositionDepartmentResponse{
			ID:           d.ID,
			DepartmentID: d.DepartmentID,
			CreatedAt:    d.CreatedAt,
		}

		// Include department details if preloaded
		if d.Department != nil {
			resp.Department = &contract.DepartmentResponse{
				ID:   d.Department.ID,
				Name: d.Department.Name,
				Code: d.Department.Code,
				Path: d.Department.Path,
			}
		}

		out = append(out, resp)
	}
	return out
}

func DispositionActionSelectionsToContract(list []entities.LetterDispositionActionSelection) []contract.DispositionActionSelectionResponse {
	out := make([]contract.DispositionActionSelectionResponse, 0, len(list))
	for _, d := range list {
		resp := contract.DispositionActionSelectionResponse{
			ID:        d.ID,
			ActionID:  d.ActionID,
			Action:    nil, // Will be populated by processor
			Note:      d.Note,
			CreatedBy: d.CreatedBy,
			CreatedAt: d.CreatedAt,
		}
		out = append(out, resp)
	}
	return out
}

func DispositionActionSelectionsWithDetailsToContract(list []entities.LetterDispositionActionSelection) []contract.DispositionActionSelectionResponse {
	out := make([]contract.DispositionActionSelectionResponse, 0, len(list))
	for _, d := range list {
		resp := contract.DispositionActionSelectionResponse{
			ID:        d.ID,
			ActionID:  d.ActionID,
			Action:    nil, // Will be populated by processor
			Note:      d.Note,
			CreatedBy: d.CreatedBy,
			CreatedAt: d.CreatedAt,
		}

		// Include action details if preloaded
		if d.Action != nil {
			resp.Action = &contract.DispositionActionResponse{
				ID:           d.Action.ID.String(),
				Code:         d.Action.Code,
				Label:        d.Action.Label,
				Description:  d.Action.Description,
				RequiresNote: d.Action.RequiresNote,
				GroupName:    d.Action.GroupName,
				SortOrder:    d.Action.SortOrder,
				IsActive:     d.Action.IsActive,
				CreatedAt:    d.Action.CreatedAt,
				UpdatedAt:    d.Action.UpdatedAt,
			}
		}

		out = append(out, resp)
	}
	return out
}

func DispositionNotesToContract(list []entities.DispositionNote) []contract.DispositionNoteResponse {
	out := make([]contract.DispositionNoteResponse, 0, len(list))
	for _, d := range list {
		resp := contract.DispositionNoteResponse{
			ID:        d.ID,
			UserID:    d.UserID,
			Note:      d.Note,
			CreatedAt: d.CreatedAt,
		}
		out = append(out, resp)
	}
	return out
}

func DispositionNotesWithDetailsToContract(list []entities.DispositionNote) []contract.DispositionNoteResponse {
	out := make([]contract.DispositionNoteResponse, 0, len(list))
	for _, d := range list {
		resp := contract.DispositionNoteResponse{
			ID:        d.ID,
			UserID:    d.UserID,
			Note:      d.Note,
			CreatedAt: d.CreatedAt,
		}

		// Include user details if preloaded
		if d.User != nil {
			resp.User = &contract.UserResponse{
				ID:        d.User.ID,
				Name:      d.User.Name,
				Email:     d.User.Email,
				IsActive:  d.User.IsActive,
				CreatedAt: d.User.CreatedAt,
				UpdatedAt: d.User.UpdatedAt,
			}
		}

		out = append(out, resp)
	}
	return out
}

func DiscussionEntityToContract(e *entities.LetterDiscussion) *contract.LetterDiscussionResponse {
	var mentions map[string]interface{}
	if e.Mentions != nil {
		mentions = map[string]interface{}(e.Mentions)
	}
	return &contract.LetterDiscussionResponse{
		ID:        e.ID,
		LetterID:  e.LetterID,
		ParentID:  e.ParentID,
		UserID:    e.UserID,
		Message:   e.Message,
		Mentions:  mentions,
		CreatedAt: e.CreatedAt,
		UpdatedAt: e.UpdatedAt,
		EditedAt:  e.EditedAt,
	}
}

func DiscussionsWithPreloadedDataToContract(list []entities.LetterDiscussion, mentionedUsers []entities.User) []contract.LetterDiscussionResponse {
	// Create a map for efficient user lookup
	userMap := make(map[uuid.UUID]entities.User)
	for _, user := range mentionedUsers {
		userMap[user.ID] = user
	}

	out := make([]contract.LetterDiscussionResponse, 0, len(list))
	for _, d := range list {
		resp := contract.LetterDiscussionResponse{
			ID:        d.ID,
			LetterID:  d.LetterID,
			ParentID:  d.ParentID,
			UserID:    d.UserID,
			Message:   d.Message,
			Mentions:  map[string]interface{}(d.Mentions),
			CreatedAt: d.CreatedAt,
			UpdatedAt: d.UpdatedAt,
			EditedAt:  d.EditedAt,
		}

		// Include user profile if preloaded
		if d.User != nil {
			resp.User = &contract.UserResponse{
				ID:        d.User.ID,
				Name:      d.User.Name,
				Email:     d.User.Email,
				IsActive:  d.User.IsActive,
				CreatedAt: d.User.CreatedAt,
				UpdatedAt: d.User.UpdatedAt,
			}

			// Include user profile if available
			if d.User.Profile != nil {
				resp.User.Profile = &contract.UserProfileResponse{
					UserID:      d.User.Profile.UserID,
					FullName:    d.User.Profile.FullName,
					DisplayName: d.User.Profile.DisplayName,
					Phone:       d.User.Profile.Phone,
					AvatarURL:   d.User.Profile.AvatarURL,
					JobTitle:    d.User.Profile.JobTitle,
					EmployeeNo:  d.User.Profile.EmployeeNo,
					Bio:         d.User.Profile.Bio,
					Timezone:    d.User.Profile.Timezone,
					Locale:      d.User.Profile.Locale,
				}
			}
		}

		// Process mentions to get mentioned users with profiles
		if d.Mentions != nil {
			mentions := map[string]interface{}(d.Mentions)
			if userIDs, ok := mentions["user_ids"]; ok {
				if userIDList, ok := userIDs.([]interface{}); ok {
					mentionedUsersList := make([]contract.UserResponse, 0)
					for _, userID := range userIDList {
						if userIDStr, ok := userID.(string); ok {
							if userUUID, err := uuid.Parse(userIDStr); err == nil {
								if user, exists := userMap[userUUID]; exists {
									userResp := contract.UserResponse{
										ID:        user.ID,
										Name:      user.Name,
										Email:     user.Email,
										IsActive:  user.IsActive,
										CreatedAt: user.CreatedAt,
										UpdatedAt: user.UpdatedAt,
									}

									// Include user profile if available
									if user.Profile != nil {
										userResp.Profile = &contract.UserProfileResponse{
											UserID:      user.Profile.UserID,
											FullName:    user.Profile.FullName,
											DisplayName: user.Profile.DisplayName,
											Phone:       user.Profile.Phone,
											AvatarURL:   user.Profile.AvatarURL,
											JobTitle:    user.Profile.JobTitle,
											EmployeeNo:  user.Profile.EmployeeNo,
											Bio:         user.Profile.Bio,
											Timezone:    user.Profile.Timezone,
											Locale:      user.Profile.Locale,
										}
									}
									mentionedUsersList = append(mentionedUsersList, userResp)
								}
							}
						}
					}
					resp.MentionedUsers = mentionedUsersList
				}
			}
		}

		out = append(out, resp)
	}
	return out
}

func EnhancedDispositionsWithPreloadedDataToContract(list []entities.LetterIncomingDisposition) []contract.EnhancedDispositionResponse {
	out := make([]contract.EnhancedDispositionResponse, 0, len(list))
	for _, d := range list {
		resp := contract.EnhancedDispositionResponse{
			ID:               d.ID,
			LetterID:         d.LetterID,
			DepartmentID:     d.DepartmentID,
			Notes:            d.Notes,
			ReadAt:           d.ReadAt,
			CreatedBy:        d.CreatedBy,
			CreatedAt:        d.CreatedAt,
			UpdatedAt:        d.UpdatedAt,
			Departments:      []contract.DispositionDepartmentResponse{},
			Actions:          []contract.DispositionActionSelectionResponse{},
			DispositionNotes: []contract.DispositionNoteResponse{},
			Department:       DepartmentToContract(d.Department),
		}

		if len(d.Departments) > 0 {
			resp.Departments = DispositionDepartmentsWithDetailsToContract(d.Departments)
		}

		// Include preloaded action selections with details
		if len(d.ActionSelections) > 0 {
			resp.Actions = DispositionActionSelectionsWithDetailsToContract(d.ActionSelections)
		}

		// Include preloaded notes with user details
		if len(d.DispositionNotes) > 0 {
			resp.DispositionNotes = DispositionNotesWithDetailsToContract(d.DispositionNotes)
		}

		out = append(out, resp)
	}
	return out
}
