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"
	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 chatScheduleService struct {
	chatScheduleRepository repository.ChatScheduleRepository
	chatRepository         repository.ChatRepository
	chatService            ChatService
	chatScheduleMapper     *mapper.ChatScheduleMapper
	Log                    zerolog.Logger
	UsersRepo              usersRepository.UsersRepository
}

type ChatScheduleService interface {
	// Chat Schedule CRUD operations
	CreateChatSchedule(authToken string, req request.ChatScheduleCreateRequest) (dataResult *mapper.ChatScheduleResponse, err error)
	GetAllChatSchedules(authToken string, req request.ChatScheduleQueryRequest) (schedules []*mapper.ChatScheduleResponse, paging paginator.Pagination, err error)
	GetChatScheduleByID(authToken string, id uint) (schedule *mapper.ChatScheduleResponse, err error)
	UpdateChatSchedule(authToken string, id uint, req request.ChatScheduleUpdateRequest) (err error)
	DeleteChatSchedule(authToken string, id uint) (err error)

	// Additional schedule operations
	GetUpcomingSchedules(authToken string, limit int) (schedules []*mapper.ChatScheduleResponse, err error)
	GetSchedulesByStatus(authToken string, status string) (schedules []*mapper.ChatScheduleResponse, err error)
	SendScheduleReminder(authToken string, scheduleID uint) (err error)
}

func NewChatScheduleService(
	chatScheduleRepository repository.ChatScheduleRepository,
	chatRepository repository.ChatRepository,
	chatService ChatService,
	log zerolog.Logger,
	usersRepo usersRepository.UsersRepository,
) ChatScheduleService {
	return &chatScheduleService{
		chatScheduleRepository: chatScheduleRepository,
		chatRepository:         chatRepository,
		chatService:            chatService,
		chatScheduleMapper:     mapper.NewChatScheduleMapper(),
		Log:                    log,
		UsersRepo:              usersRepo,
	}
}

// CreateChatSchedule - Create a new chat schedule
func (_i *chatScheduleService) CreateChatSchedule(authToken string, req request.ChatScheduleCreateRequest) (dataResult *mapper.ChatScheduleResponse, err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return nil, errors.New("user not found")
	}
	userID := userInfo.ID

	// Validate that the scheduled time is in the future
	if req.ScheduledAt.Before(time.Now()) {
		return nil, errors.New("scheduled time must be in the future")
	}

	var chatSessionID uint

	// If ChatSessionID is not provided, create a new chat session
	if req.ChatSessionID == nil {
		// Create a new personal chat session for the schedule
		chatSessionReq := request.ChatSessionCreateRequest{
			Name:    &req.Title, // Use schedule title as chat session name
			Type:    "group",    // Default to group chat for schedules
			UserIDs: []uint{},   // Empty for now, can be populated later
		}

		chatSession, err := _i.chatService.CreateChatSession(authToken, chatSessionReq)
		if err != nil {
			return nil, errors.New("failed to create chat session: " + err.Error())
		}

		chatSessionID = chatSession.ID
	} else {
		chatSessionID = *req.ChatSessionID

		// Check if user is participant in the existing chat session
		isParticipant, err := _i.chatRepository.CheckUserInChatSession(userID, chatSessionID)
		if err != nil {
			return nil, err
		}

		if !isParticipant {
			return nil, errors.New("user is not a participant in this chat session")
		}
	}

	// Convert request to entity
	schedule := _i.chatScheduleMapper.ToEntity(req)
	schedule.ChatSessionID = chatSessionID
	schedule.CreatedBy = userID

	// Create schedule
	result, err := _i.chatScheduleRepository.CreateChatSchedule(schedule)
	if err != nil {
		return nil, err
	}

	// Convert to response
	dataResult = _i.chatScheduleMapper.ToResponse(result)
	return
}

// GetAllChatSchedules - Get all chat schedules for a user
func (_i *chatScheduleService) GetAllChatSchedules(authToken string, req request.ChatScheduleQueryRequest) (schedules []*mapper.ChatScheduleResponse, 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")
	}
	userID := userInfo.ID

	// Get schedules from repository
	scheduleEntities, paging, err := _i.chatScheduleRepository.GetAllChatSchedules(userID, req)
	if err != nil {
		return nil, paginator.Pagination{}, err
	}

	// Convert to response
	schedules = _i.chatScheduleMapper.ToResponseList(scheduleEntities)
	return
}

// GetChatScheduleByID - Get a specific chat schedule
func (_i *chatScheduleService) GetChatScheduleByID(authToken string, id uint) (schedule *mapper.ChatScheduleResponse, err error) {
	// Get schedule from repository
	scheduleEntity, err := _i.chatScheduleRepository.GetChatScheduleByID(id)
	if err != nil {
		return nil, err
	}

	// Convert to response
	schedule = _i.chatScheduleMapper.ToResponse(scheduleEntity)
	return
}

// UpdateChatSchedule - Update a chat schedule
func (_i *chatScheduleService) UpdateChatSchedule(authToken string, id uint, req request.ChatScheduleUpdateRequest) (err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return errors.New("user not found")
	}
	userID := userInfo.ID

	// Check if user is participant in the chat session
	isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, id)
	if err != nil {
		return err
	}

	if !isParticipant {
		return errors.New("user is not a participant in this chat session")
	}

	// Get existing schedule to check if user is the creator
	existingSchedule, err := _i.chatScheduleRepository.GetChatScheduleByID(id)
	if err != nil {
		return err
	}

	// Only creator can update the schedule
	if existingSchedule.CreatedBy != userID {
		return errors.New("only the creator can update this schedule")
	}

	// Validate scheduled time if provided
	if !req.ScheduledAt.IsZero() && req.ScheduledAt.Before(time.Now()) {
		return errors.New("scheduled time must be in the future")
	}

	// Convert request to entity
	schedule := _i.chatScheduleMapper.ToUpdateEntity(req)

	// Update schedule
	err = _i.chatScheduleRepository.UpdateChatSchedule(id, schedule)
	return
}

// DeleteChatSchedule - Delete a chat schedule
func (_i *chatScheduleService) DeleteChatSchedule(authToken string, id uint) (err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return errors.New("user not found")
	}
	userID := userInfo.ID

	// Get existing schedule to check if user is the creator
	existingSchedule, err := _i.chatScheduleRepository.GetChatScheduleByID(id)
	if err != nil {
		return err
	}

	// Only creator can delete the schedule
	if existingSchedule.CreatedBy != userID {
		return errors.New("only the creator can delete this schedule")
	}

	// Delete schedule
	err = _i.chatScheduleRepository.DeleteChatSchedule(id)
	return
}

// GetUpcomingSchedules - Get upcoming schedules for a user
func (_i *chatScheduleService) GetUpcomingSchedules(authToken string, limit int) (schedules []*mapper.ChatScheduleResponse, err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return nil, errors.New("user not found")
	}
	userID := userInfo.ID

	// Get upcoming schedules from repository
	scheduleEntities, err := _i.chatScheduleRepository.GetUpcomingSchedules(userID, limit)
	if err != nil {
		return nil, err
	}

	// Convert to response
	schedules = _i.chatScheduleMapper.ToResponseList(scheduleEntities)
	return
}

// GetSchedulesByStatus - Get schedules by status
func (_i *chatScheduleService) GetSchedulesByStatus(authToken string, status string) (schedules []*mapper.ChatScheduleResponse, err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return nil, errors.New("user not found")
	}
	userID := userInfo.ID

	// Validate status
	validStatuses := []string{"scheduled", "ongoing", "completed", "cancelled"}
	isValidStatus := false
	for _, validStatus := range validStatuses {
		if status == validStatus {
			isValidStatus = true
			break
		}
	}

	if !isValidStatus {
		return nil, errors.New("invalid status")
	}

	// Get schedules by status from repository
	scheduleEntities, err := _i.chatScheduleRepository.GetSchedulesByStatus(status)
	if err != nil {
		return nil, err
	}

	// Filter by user participation
	var userSchedules []*entity.ChatSchedules
	for _, schedule := range scheduleEntities {
		isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, schedule.ID)
		if err != nil {
			continue
		}
		if isParticipant {
			userSchedules = append(userSchedules, schedule)
		}
	}

	// Convert to response
	schedules = _i.chatScheduleMapper.ToResponseList(userSchedules)
	return
}

// SendScheduleReminder - Send reminder for a schedule
func (_i *chatScheduleService) SendScheduleReminder(authToken string, scheduleID uint) (err error) {
	userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if userInfo == nil {
		return errors.New("user not found")
	}
	userID := userInfo.ID

	// Get schedule
	schedule, err := _i.chatScheduleRepository.GetChatScheduleByID(scheduleID)
	if err != nil {
		return err
	}

	// Only creator can send reminders
	if schedule.CreatedBy != userID {
		return errors.New("only the creator can send reminders")
	}

	// Check if reminder was already sent
	if schedule.IsReminderSent {
		return errors.New("reminder has already been sent")
	}

	// TODO: Implement actual reminder sending logic (email, push notification, etc.)
	// For now, just update the reminder status
	now := time.Now()
	schedule.IsReminderSent = true
	schedule.ReminderSentAt = &now

	err = _i.chatScheduleRepository.UpdateChatSchedule(scheduleID, schedule)
	return
}
