package service

import (
	"errors"
	"narasi-ahli-be/app/database/entity"
	"narasi-ahli-be/app/module/chat/mapper"
	"narasi-ahli-be/app/module/chat/repository"
	"narasi-ahli-be/app/module/chat/request"
	"narasi-ahli-be/app/module/chat/response"
	usersRepository "narasi-ahli-be/app/module/users/repository"
	"narasi-ahli-be/utils/paginator"
	utilSvc "narasi-ahli-be/utils/service"
	"time"

	"github.com/rs/zerolog"
)

type chatService struct {
	Repo      repository.ChatRepository
	UsersRepo usersRepository.UsersRepository
	Log       zerolog.Logger
}

type ChatService interface {
	// Chat Session methods
	GetAllChatSessions(authToken string, req request.ChatSessionQueryRequest) (chatSessions []*response.ChatSessionResponse, paging paginator.Pagination, err error)
	GetChatSessionByID(authToken string, id uint) (chatSession *response.ChatSessionResponse, err error)
	CreateChatSession(authToken string, req request.ChatSessionCreateRequest) (chatSession *response.ChatSessionResponse, err error)
	UpdateChatSession(authToken string, id uint, req request.ChatSessionUpdateRequest) (err error)
	DeleteChatSession(authToken string, id uint) error

	// Chat Message methods
	GetAllChatMessages(authToken string, req request.ChatMessageQueryRequest) (chatMessages []*response.ChatMessageResponse, paging paginator.Pagination, err error)
	GetChatMessageByID(authToken string, id uint) (chatMessage *response.ChatMessageResponse, err error)
	CreateChatMessage(authToken string, req request.ChatMessageCreateRequest) (chatMessage *response.ChatMessageResponse, err error)
	UpdateChatMessage(authToken string, id uint, req request.ChatMessageUpdateRequest) (err error)
	DeleteChatMessage(authToken string, id uint) error

	// Chat Participant methods
	AddParticipantToChat(authToken string, chatSessionID uint, participantUserID uint) error
	RemoveParticipantFromChat(authToken string, chatSessionID uint, participantUserID uint) error
}

func NewChatService(repo repository.ChatRepository, usersRepo usersRepository.UsersRepository, log zerolog.Logger) ChatService {
	return &chatService{
		Repo:      repo,
		UsersRepo: usersRepo,
		Log:       log,
	}
}

// Chat Session Service Methods
func (_i *chatService) GetAllChatSessions(authToken string, req request.ChatSessionQueryRequest) (chatSessions []*response.ChatSessionResponse, paging paginator.Pagination, err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return nil, paginator.Pagination{}, errors.New("user not found")
	}

	results, paging, err := _i.Repo.GetAllChatSessions(userInfo.ID, req)
	if err != nil {
		return
	}

	for _, result := range results {
		chatSessions = append(chatSessions, mapper.ChatSessionResponseMapper(result))
	}

	return
}

func (_i *chatService) GetChatSessionByID(authToken string, id uint) (chatSession *response.ChatSessionResponse, err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return nil, errors.New("user not found")
	}

	result, err := _i.Repo.FindChatSessionByUserAndID(userInfo.ID, id)
	if err != nil {
		return nil, err
	}
	if result == nil {
		return nil, errors.New("chat session not found or access denied")
	}

	return mapper.ChatSessionResponseMapper(result), nil
}

func (_i *chatService) CreateChatSession(authToken string, req request.ChatSessionCreateRequest) (chatSession *response.ChatSessionResponse, err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return nil, errors.New("user not found")
	}

	_i.Log.Info().Interface("data", req).Msg("Creating chat session")

	// Validate business rules
	if req.Type == "personal" && len(req.UserIDs) != 1 {
		return nil, errors.New("personal chat must have exactly one other participant")
	}
	if req.Type == "group" && len(req.UserIDs) < 1 {
		return nil, errors.New("group chat must have at least one participant")
	}

	// Check if personal chat already exists
	if req.Type == "personal" {
		existingChat, err := _i.Repo.FindPersonalChatSession(userInfo.ID, req.UserIDs[0])
		if err == nil && existingChat != nil {
			return mapper.ChatSessionResponseMapper(existingChat), nil
		}
	}

	// Validate all user IDs exist
	for _, userID := range req.UserIDs {
		user, err := _i.UsersRepo.FindOne(userID)
		if err != nil || user == nil {
			return nil, errors.New("invalid user ID: " + string(rune(userID)))
		}
	}

	// Create chat session
	entity := req.ToEntity(userInfo.ID)
	result, err := _i.Repo.CreateChatSession(entity)
	if err != nil {
		return nil, err
	}

	// Add creator as participant
	creatorParticipant := &request.ChatParticipantCreateRequest{
		ChatSessionID: result.ID,
		UserID:        userInfo.ID,
	}
	_, err = _i.Repo.CreateChatParticipant(creatorParticipant.ToEntity())
	if err != nil {
		return nil, err
	}

	// Add other participants
	for _, userID := range req.UserIDs {
		participant := &request.ChatParticipantCreateRequest{
			ChatSessionID: result.ID,
			UserID:        userID,
		}
		_, err = _i.Repo.CreateChatParticipant(participant.ToEntity())
		if err != nil {
			return nil, err
		}
	}

	// Reload with all relationships
	finalResult, err := _i.Repo.FindChatSessionByID(result.ID)
	if err != nil {
		return nil, err
	}

	return mapper.ChatSessionResponseMapper(finalResult), nil
}

func (_i *chatService) UpdateChatSession(authToken string, id uint, req request.ChatSessionUpdateRequest) (err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return errors.New("user not found")
	}

	_i.Log.Info().Interface("data", req).Msg("Updating chat session")

	// Check if chat session exists and user has access
	existing, err := _i.Repo.FindChatSessionByUserAndID(userInfo.ID, id)
	if err != nil {
		return err
	}
	if existing == nil {
		return errors.New("chat session not found or access denied")
	}

	// Only creator can update chat session
	if existing.CreatedBy != userInfo.ID {
		return errors.New("only chat creator can update chat session")
	}

	entity := req.ToEntity()
	return _i.Repo.UpdateChatSession(id, entity)
}

func (_i *chatService) DeleteChatSession(authToken string, id uint) error {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return errors.New("user not found")
	}

	_i.Log.Info().Uint("userId", userInfo.ID).Uint("id", id).Msg("Deleting chat session")

	// Check if chat session exists and user has access
	existing, err := _i.Repo.FindChatSessionByUserAndID(userInfo.ID, id)
	if err != nil {
		return err
	}
	if existing == nil {
		return errors.New("chat session not found or access denied")
	}

	// Only creator can delete chat session
	if existing.CreatedBy != userInfo.ID {
		return errors.New("only chat creator can delete chat session")
	}

	return _i.Repo.DeleteChatSession(id)
}

// Chat Message Service Methods
func (_i *chatService) GetAllChatMessages(authToken string, req request.ChatMessageQueryRequest) (chatMessages []*response.ChatMessageResponse, paging paginator.Pagination, err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return nil, paginator.Pagination{}, errors.New("user not found")
	}

	// Set user ID in request for repository
	req.UserID = userInfo.ID

	results, paging, err := _i.Repo.GetAllChatMessages(req)
	if err != nil {
		return
	}

	for _, result := range results {
		chatMessages = append(chatMessages, mapper.ChatMessageResponseMapper(result))
	}

	return
}

func (_i *chatService) GetChatMessageByID(authToken string, id uint) (chatMessage *response.ChatMessageResponse, err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return nil, errors.New("user not found")
	}

	result, err := _i.Repo.FindChatMessageByUserAndID(userInfo.ID, id)
	if err != nil {
		return nil, err
	}
	if result == nil {
		return nil, errors.New("chat message not found or access denied")
	}

	return mapper.ChatMessageResponseMapper(result), nil
}

func (_i *chatService) CreateChatMessage(authToken string, req request.ChatMessageCreateRequest) (chatMessage *response.ChatMessageResponse, err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return nil, errors.New("user not found")
	}

	_i.Log.Info().Interface("data", req).Msg("Creating chat message")

	// Check if user is participant in the chat session
	isParticipant, err := _i.Repo.CheckUserInChatSession(userInfo.ID, req.ChatSessionID)
	if err != nil {
		return nil, err
	}
	if !isParticipant {
		return nil, errors.New("user is not a participant in this chat session")
	}

	entity := req.ToEntity(userInfo.ID)
	result, err := _i.Repo.CreateChatMessage(entity)
	if err != nil {
		return nil, err
	}

	return mapper.ChatMessageResponseMapper(result), nil
}

func (_i *chatService) UpdateChatMessage(authToken string, id uint, req request.ChatMessageUpdateRequest) (err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return errors.New("user not found")
	}

	_i.Log.Info().Interface("data", req).Msg("Updating chat message")

	// Check if message exists and user has access
	existing, err := _i.Repo.FindChatMessageByUserAndID(userInfo.ID, id)
	if err != nil {
		return err
	}
	if existing == nil {
		return errors.New("chat message not found or access denied")
	}

	// Only sender can update message
	if existing.SenderID != userInfo.ID {
		return errors.New("only message sender can update message")
	}

	now := time.Now()
	entity := req.ToEntity()
	entity.EditedAt = &now
	return _i.Repo.UpdateChatMessage(id, entity)
}

func (_i *chatService) DeleteChatMessage(authToken string, id uint) error {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return errors.New("user not found")
	}

	_i.Log.Info().Uint("userId", userInfo.ID).Uint("id", id).Msg("Deleting chat message")

	// Check if message exists and user has access
	existing, err := _i.Repo.FindChatMessageByUserAndID(userInfo.ID, id)
	if err != nil {
		return err
	}
	if existing == nil {
		return errors.New("chat message not found or access denied")
	}

	// Only sender can delete message
	if existing.SenderID != userInfo.ID {
		return errors.New("only message sender can delete message")
	}

	return _i.Repo.DeleteChatMessage(id)
}

// Chat Participant Service Methods
func (_i *chatService) AddParticipantToChat(authToken string, chatSessionID uint, participantUserID uint) error {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return errors.New("user not found")
	}

	_i.Log.Info().Uint("userId", userInfo.ID).Uint("chatSessionID", chatSessionID).Uint("participantUserID", participantUserID).Msg("Adding participant to chat")

	// Check if user has access to chat session
	existing, err := _i.Repo.FindChatSessionByUserAndID(userInfo.ID, chatSessionID)
	if err != nil {
		return err
	}
	if existing == nil {
		return errors.New("chat session not found or access denied")
	}

	// Only creator can add participants
	if existing.CreatedBy != userInfo.ID {
		return errors.New("only chat creator can add participants")
	}

	// Validate participant user exists
	user, err := _i.UsersRepo.FindOne(participantUserID)
	if err != nil || user == nil {
		return errors.New("invalid user ID")
	}

	// Check if user is already a participant
	isParticipant, err := _i.Repo.CheckUserInChatSession(participantUserID, chatSessionID)
	if err != nil {
		return err
	}
	if isParticipant {
		return errors.New("user is already a participant in this chat")
	}

	participant := &request.ChatParticipantCreateRequest{
		ChatSessionID: chatSessionID,
		UserID:        participantUserID,
	}
	_, err = _i.Repo.CreateChatParticipant(participant.ToEntity())
	return err
}

func (_i *chatService) RemoveParticipantFromChat(authToken string, chatSessionID uint, participantUserID uint) error {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return errors.New("user not found")
	}

	_i.Log.Info().Uint("userId", userInfo.ID).Uint("chatSessionID", chatSessionID).Uint("participantUserID", participantUserID).Msg("Removing participant from chat")

	// Check if user has access to chat session
	existing, err := _i.Repo.FindChatSessionByUserAndID(userInfo.ID, chatSessionID)
	if err != nil {
		return err
	}
	if existing == nil {
		return errors.New("chat session not found or access denied")
	}

	// Only creator can remove participants (or user can remove themselves)
	if existing.CreatedBy != userInfo.ID && userInfo.ID != participantUserID {
		return errors.New("only chat creator can remove participants or user can remove themselves")
	}

	// Find participant
	participants, err := _i.Repo.FindChatParticipantsBySessionID(chatSessionID)
	if err != nil {
		return err
	}

	var participantToRemove *entity.ChatParticipants
	for _, participant := range participants {
		if participant.UserID == participantUserID {
			participantToRemove = participant
			break
		}
	}

	if participantToRemove == nil {
		return errors.New("participant not found")
	}

	// Soft delete by setting is_active to false
	now := time.Now()
	participantToRemove.IsActive = false
	participantToRemove.LeftAt = &now
	return _i.Repo.UpdateChatParticipant(participantToRemove.ID, participantToRemove)
}
