package services

import (
	"context"
	"errors"
	"fmt"
	"time" // Add time import

	"veza-backend-api/internal/models"
	"veza-backend-api/internal/repositories"

	"github.com/google/uuid" // Add uuid import
	"go.uber.org/zap"
	"gorm.io/gorm"
)

// RoomService gère la logique métier pour les rooms
type RoomService struct {
	roomRepo    *repositories.RoomRepository
	messageRepo *repositories.ChatMessageRepository
	logger      *zap.Logger
}

// NewRoomService crée une nouvelle instance de RoomService
func NewRoomService(roomRepo *repositories.RoomRepository, messageRepo *repositories.ChatMessageRepository, logger *zap.Logger) *RoomService {
	return &RoomService{
		roomRepo:    roomRepo,
		messageRepo: messageRepo,
		logger:      logger,
	}
}

// CreateRoomRequest représente une requête de création de room
type CreateRoomRequest struct {
	Name        string  `json:"name" binding:"required,min=1,max=255"`
	Description *string `json:"description,omitempty"`
	Type        string  `json:"type" binding:"required,oneof=public private direct"`
	IsPrivate   bool    `json:"is_private"`
}

// RoomResponse représente une réponse de room pour l'API
// MIGRATION UUID: CreatedBy et Participants migrés vers UUID
type RoomResponse struct {
	ID           uuid.UUID   `json:"id"`
	Name         string      `json:"name"`
	Description  string      `json:"description,omitempty"`
	Type         string      `json:"type"`
	IsPrivate    bool        `json:"is_private"`
	CreatedBy    *uuid.UUID  `json:"created_by"`
	Participants []uuid.UUID `json:"participants"`
	CreatedAt    string      `json:"created_at"`
	UpdatedAt    string      `json:"updated_at"`
}

// CreateRoom crée une nouvelle room
func (s *RoomService) CreateRoom(ctx context.Context, userID uuid.UUID, req CreateRoomRequest) (*RoomResponse, error) {
	if req.Name == "" {
		return nil, errors.New("room name is required")
	}

	// Créer la room
	room := &models.Room{
		Name:        req.Name,
		Description: "",
		Type:        req.Type,
		IsPrivate:   req.IsPrivate,
		CreatedBy:   userID, // Corrected: userID is uuid.UUID, models.Room.CreatedBy is uuid.UUID
	}

	if req.Description != nil {
		room.Description = *req.Description
	}

	if err := s.roomRepo.Create(ctx, room); err != nil {
		s.logger.Error("failed to create room",
			zap.Error(err),
			zap.String("user_id", userID.String()),
			zap.String("room_name", req.Name))
		return nil, fmt.Errorf("failed to create room: %w", err)
	}

	// Ajouter le créateur comme membre admin
	member := &models.RoomMember{
		RoomID: room.ID, // use uuid
		UserID: userID,
		Role:   "admin",
	}

	if err := s.roomRepo.AddMember(ctx, member); err != nil {
		s.logger.Error("failed to add creator as room member",
			zap.Error(err),
			zap.String("room_id", room.ID.String()),
			zap.String("user_id", userID.String()))
		// Ne pas retourner d'erreur, la room est créée
	}

	s.logger.Info("room created successfully",
		zap.String("room_id", room.ID.String()),
		zap.String("user_id", userID.String()),
		zap.String("room_name", room.Name))

	return &RoomResponse{
		ID:           room.ID,
		Name:         room.Name,
		Description:  room.Description,
		Type:         room.Type,
		IsPrivate:    room.IsPrivate,
		CreatedBy:    &room.CreatedBy, // Corrected: & to get pointer to uuid.UUID
		Participants: []uuid.UUID{userID},
		CreatedAt:    room.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
		UpdatedAt:    room.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
	}, nil
}

// GetUserRooms récupère toutes les rooms d'un utilisateur
func (s *RoomService) GetUserRooms(ctx context.Context, userID uuid.UUID) ([]*RoomResponse, error) {
	rooms, err := s.roomRepo.GetByUserID(ctx, userID)
	if err != nil {
		s.logger.Error("failed to get user rooms",
			zap.Error(err),
			zap.String("user_id", userID.String()))
		return nil, fmt.Errorf("failed to get user rooms: %w", err)
	}

	responses := make([]*RoomResponse, 0, len(rooms))
	for _, room := range rooms {
		// Récupérer les membres pour avoir la liste des participants
		members, err := s.roomRepo.GetMembersByRoomID(ctx, room.ID)
		if err != nil {
			s.logger.Warn("failed to get room members",
				zap.Error(err),
				zap.String("room_id", room.ID.String()))
			members = []*models.RoomMember{}
		}

		participants := make([]uuid.UUID, 0, len(members))
		for _, member := range members {
			participants = append(participants, member.UserID)
		}

		responses = append(responses, &RoomResponse{
			ID:           room.ID,
			Name:         room.Name,
			Description:  room.Description,
			Type:         room.Type,
			IsPrivate:    room.IsPrivate,
			CreatedBy:    &room.CreatedBy, // Corrected: & to get pointer to uuid.UUID
			Participants: participants,
			CreatedAt:    room.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
			UpdatedAt:    room.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
		})
	}

	return responses, nil
}

// GetRoom récupère une room par son ID
func (s *RoomService) GetRoom(ctx context.Context, roomID uuid.UUID) (*RoomResponse, error) {
	room, err := s.roomRepo.GetByID(ctx, roomID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrRoomNotFound
		}
		s.logger.Error("failed to get room",
			zap.Error(err),
			zap.String("room_id", roomID.String()))
		return nil, fmt.Errorf("failed to get room: %w", err)
	}

	// Récupérer les membres
	members, err := s.roomRepo.GetMembersByRoomID(ctx, roomID)
	if err != nil {
		s.logger.Warn("failed to get room members",
			zap.Error(err),
			zap.String("room_id", roomID.String()))
		members = []*models.RoomMember{}
	}

	participants := make([]uuid.UUID, 0, len(members))
	for _, member := range members {
		participants = append(participants, member.UserID)
	}

	return &RoomResponse{
		ID:           room.ID,
		Name:         room.Name,
		Description:  room.Description,
		Type:         room.Type,
		IsPrivate:    room.IsPrivate,
		CreatedBy:    &room.CreatedBy, // Corrected: & to get pointer to uuid.UUID
		Participants: participants,
		CreatedAt:    room.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
		UpdatedAt:    room.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
	}, nil
}

// AddMember ajoute un membre à une room
func (s *RoomService) AddMember(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error {
	member := &models.RoomMember{
		RoomID: roomID,
		UserID: userID,
		Role:   "member",
	}

	if err := s.roomRepo.AddMember(ctx, member); err != nil {
		s.logger.Error("failed to add member to room",
			zap.Error(err),
			zap.String("room_id", roomID.String()),
			zap.String("user_id", userID.String()))
		return fmt.Errorf("failed to add member: %w", err)
	}

	s.logger.Info("member added to room",
		zap.String("room_id", roomID.String()),
		zap.String("user_id", userID.String()))

	return nil
}

// RemoveMember retire un membre d'une room
// BE-API-011: Implement conversation participants endpoints
func (s *RoomService) RemoveMember(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error {
	// Vérifier que la room existe
	_, err := s.roomRepo.GetByID(ctx, roomID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return ErrRoomNotFound
		}
		return fmt.Errorf("failed to get room: %w", err)
	}

	// Retirer le membre
	if err := s.roomRepo.RemoveMember(ctx, roomID, userID); err != nil {
		s.logger.Error("failed to remove member from room",
			zap.Error(err),
			zap.String("room_id", roomID.String()),
			zap.String("user_id", userID.String()))
		return fmt.Errorf("failed to remove member: %w", err)
	}

	s.logger.Info("member removed from room",
		zap.String("room_id", roomID.String()),
		zap.String("user_id", userID.String()))

	return nil
}

// UpdateRoomRequest représente une requête de mise à jour de room
// BE-API-012: Implement conversation update endpoint
type UpdateRoomRequest struct {
	Name        *string `json:"name,omitempty"`
	Description *string `json:"description,omitempty"`
}

// UpdateRoom met à jour une room (nom et/ou description)
// BE-API-012: Implement conversation update endpoint
// Seul le créateur de la room ou un admin peut mettre à jour la room
func (s *RoomService) UpdateRoom(ctx context.Context, roomID uuid.UUID, userID uuid.UUID, req UpdateRoomRequest) (*RoomResponse, error) {
	// Vérifier que la room existe
	room, err := s.roomRepo.GetByID(ctx, roomID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrRoomNotFound
		}
		return nil, fmt.Errorf("failed to get room: %w", err)
	}

	// Vérifier que l'utilisateur est le créateur de la room
	if room.CreatedBy != userID {
		return nil, fmt.Errorf("forbidden: only room creator can update the room")
	}

	// Mettre à jour les champs fournis
	if req.Name != nil {
		room.Name = *req.Name
	}
	if req.Description != nil {
		room.Description = *req.Description
	}

	// Sauvegarder les modifications
	if err := s.roomRepo.Update(ctx, room); err != nil {
		s.logger.Error("failed to update room",
			zap.Error(err),
			zap.String("room_id", roomID.String()),
			zap.String("user_id", userID.String()))
		return nil, fmt.Errorf("failed to update room: %w", err)
	}

	// Récupérer les membres pour la réponse
	members, err := s.roomRepo.GetMembersByRoomID(ctx, roomID)
	if err != nil {
		s.logger.Warn("failed to get room members",
			zap.Error(err),
			zap.String("room_id", roomID.String()))
		members = []*models.RoomMember{}
	}

	participants := make([]uuid.UUID, 0, len(members))
	for _, member := range members {
		participants = append(participants, member.UserID)
	}

	s.logger.Info("room updated successfully",
		zap.String("room_id", roomID.String()),
		zap.String("user_id", userID.String()))

	return &RoomResponse{
		ID:           room.ID,
		Name:         room.Name,
		Description:  room.Description,
		Type:         room.Type,
		IsPrivate:    room.IsPrivate,
		CreatedBy:    &room.CreatedBy,
		Participants: participants,
		CreatedAt:    room.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
		UpdatedAt:    room.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
	}, nil
}

// ChatMessageResponse pour la réponse d'historique
type ChatMessageResponse struct {
	ID             uuid.UUID `json:"id"`
	ConversationID uuid.UUID `json:"conversation_id"`
	SenderID       uuid.UUID `json:"sender_id"`
	Content        string    `json:"content"`
	MessageType    string    `json:"message_type"`
	CreatedAt      time.Time `json:"created_at"`
}

// GetRoomHistory récupère l'historique des messages d'une room
func (s *RoomService) GetRoomHistory(ctx context.Context, roomID uuid.UUID, limit, offset int) ([]ChatMessageResponse, error) {
	messages, err := s.messageRepo.GetConversationMessages(ctx, roomID, limit, offset)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) || err.Error() == "conversation not found" {
			// Check if room exists first? Assuming Repo handles it or we could use GetRoom logic
			// If messageRepo returns error on room not found
			return nil, ErrRoomNotFound
		}
		s.logger.Error("failed to get room history",
			zap.Error(err),
			zap.String("room_id", roomID.String()))
		return nil, fmt.Errorf("failed to get room history: %w", err)
	}

	responses := make([]ChatMessageResponse, len(messages))
	for i, msg := range messages {
		responses[i] = ChatMessageResponse{
			ID:             msg.ID,
			ConversationID: msg.ConversationID,
			SenderID:       msg.SenderID,
			Content:        msg.Content,
			MessageType:    msg.MessageType,
			CreatedAt:      msg.CreatedAt,
		}
	}
	return responses, nil
}

// DeleteRoom supprime une room (soft delete)
// BE-API-010: Implement conversation delete endpoint
// Seul le créateur de la room ou un admin peut supprimer la room
func (s *RoomService) DeleteRoom(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error {
	// Vérifier que la room existe
	room, err := s.roomRepo.GetByID(ctx, roomID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return ErrRoomNotFound
		}
		return fmt.Errorf("failed to get room: %w", err)
	}

	// Vérifier que l'utilisateur est le créateur de la room
	if room.CreatedBy != userID {
		return fmt.Errorf("forbidden: only room creator can delete the room")
	}

	// Supprimer la room (soft delete via GORM)
	if err := s.roomRepo.Delete(ctx, roomID); err != nil {
		s.logger.Error("failed to delete room",
			zap.Error(err),
			zap.String("room_id", roomID.String()),
			zap.String("user_id", userID.String()))
		return fmt.Errorf("failed to delete room: %w", err)
	}

	s.logger.Info("room deleted successfully",
		zap.String("room_id", roomID.String()),
		zap.String("user_id", userID.String()))

	return nil
}
