package grpc

import (
	"context"

	"github.com/sorti/openspeak/internal/presence"
	pb "github.com/sorti/openspeak/pkg/api/openspeak/v1"
)

// PresenceServiceServer implements the PresenceService gRPC service
type PresenceServiceServer struct {
	pb.UnimplementedPresenceServiceServer
	server *Server
}

// NewPresenceServiceServer creates a new PresenceServiceServer
func NewPresenceServiceServer(s *Server) *PresenceServiceServer {
	return &PresenceServiceServer{
		server: s,
	}
}

// GetMyPresence returns the current user's presence
func (p *PresenceServiceServer) GetMyPresence(ctx context.Context, req *pb.GetPresenceRequest) (*pb.UserPresence, error) {
	userID := extractUserIDFromContext(ctx)
	if userID == "" {
		return nil, ErrUnauthorized
	}

	session, err := p.server.presenceManager.GetSession(userID)
	if err != nil {
		return nil, ErrUserNotFound
	}

	return convertSessionToProto(session), nil
}

// GetUserPresence returns another user's presence
func (p *PresenceServiceServer) GetUserPresence(ctx context.Context, req *pb.GetPresenceRequest) (*pb.UserPresence, error) {
	if req.UserId == "" {
		return nil, ErrInvalidUser
	}

	session, err := p.server.presenceManager.GetSession(req.UserId)
	if err != nil {
		return nil, ErrUserNotFound
	}

	return convertSessionToProto(session), nil
}

// ListOnlineUsers returns all online users
func (p *PresenceServiceServer) ListOnlineUsers(ctx context.Context, req *pb.ListOnlineUsersRequest) (*pb.ListOnlineUsersResponse, error) {
	// This would require exposing all sessions from the presence manager
	// For now, return empty list as placeholder
	return &pb.ListOnlineUsersResponse{
		Users: make([]*pb.UserPresence, 0),
	}, nil
}

// ListChannelMembers returns members of a channel
func (p *PresenceServiceServer) ListChannelMembers(ctx context.Context, req *pb.ListChannelMembersRequest) (*pb.ListChannelMembersResponse, error) {
	if req.ChannelId == "" {
		return nil, ErrInvalidChannel
	}

	members, err := p.server.channelManager.GetChannelMembers(req.ChannelId)
	if err != nil {
		return nil, ErrChannelNotFound
	}

	userPresences := make([]*pb.UserPresence, 0, len(members))
	for _, memberID := range members {
		session, err := p.server.presenceManager.GetSession(memberID)
		if err == nil {
			userPresences = append(userPresences, convertSessionToProto(session))
		}
	}

	return &pb.ListChannelMembersResponse{
		Members: userPresences,
	}, nil
}

// SetPresenceStatus updates the user's presence status
func (p *PresenceServiceServer) SetPresenceStatus(ctx context.Context, req *pb.SetPresenceStatusRequest) (*pb.UserPresence, error) {
	userID := extractUserIDFromContext(ctx)
	if userID == "" {
		return nil, ErrUnauthorized
	}

	status := convertProtoStatusToInternal(req.Status)
	err := p.server.presenceManager.UpdatePresence(userID, status)
	if err != nil {
		return nil, ErrUserNotFound
	}

	session, err := p.server.presenceManager.GetSession(userID)
	if err != nil {
		return nil, ErrUserNotFound
	}

	return convertSessionToProto(session), nil
}

// SetMuteStatus updates the user's mute state
func (p *PresenceServiceServer) SetMuteStatus(ctx context.Context, req *pb.SetMuteStatusRequest) (*pb.UserPresence, error) {
	userID := extractUserIDFromContext(ctx)
	if userID == "" {
		return nil, ErrUnauthorized
	}

	err := p.server.presenceManager.SetMuteStatus(userID, req.MicrophoneMuted, req.SpeakerMuted)
	if err != nil {
		return nil, ErrUserNotFound
	}

	session, err := p.server.presenceManager.GetSession(userID)
	if err != nil {
		return nil, ErrUserNotFound
	}

	return convertSessionToProto(session), nil
}

// ReportActivity updates the user's last activity timestamp
func (p *PresenceServiceServer) ReportActivity(ctx context.Context, req *pb.ReportActivityRequest) (*pb.Status, error) {
	userID := extractUserIDFromContext(ctx)
	if userID == "" {
		return nil, ErrUnauthorized
	}

	session, err := p.server.presenceManager.GetSession(userID)
	if err != nil {
		return nil, ErrUserNotFound
	}

	session.UpdateActivity()

	return &pb.Status{
		Success: true,
	}, nil
}

// SubscribePresenceEvents subscribes to presence change events
func (p *PresenceServiceServer) SubscribePresenceEvents(req *pb.SubscribePresenceRequest, stream pb.PresenceService_SubscribePresenceEventsServer) error {
	// This is a streaming endpoint - would need to implement a proper event system
	// For now, return not implemented
	return ErrNotImplemented
}

// convertSessionToProto converts internal session to proto format
func convertSessionToProto(session *presence.Session) *pb.UserPresence {
	return &pb.UserPresence{
		UserId:             session.UserID,
		Status:             convertInternalStatusToProto(session.Status),
		CurrentChannelId:   session.CurrentChannelID,
		IsMicrophoneMuted:  session.IsMicrophoneMuted,
		IsSpeakerMuted:     session.IsSpeakerMuted,
		ClientVersion:      session.ClientVersion,
		Platform:           session.Platform,
		ConnectedAt:        session.ConnectedAt.Unix(),
		LastSeen:           session.LastActivityAt.Unix(),
	}
}

// convertProtoStatusToInternal converts proto status to internal format
func convertProtoStatusToInternal(status pb.PresenceStatus) presence.Status {
	switch status {
	case pb.PresenceStatus_ONLINE:
		return presence.StatusOnline
	case pb.PresenceStatus_IDLE:
		return presence.StatusIdle
	case pb.PresenceStatus_DO_NOT_DISTURB:
		return presence.StatusDoNotDisturb
	case pb.PresenceStatus_AWAY:
		return presence.StatusAway
	case pb.PresenceStatus_OFFLINE:
		return presence.StatusOffline
	default:
		return presence.StatusOnline
	}
}

// convertInternalStatusToProto converts internal status to proto format
func convertInternalStatusToProto(status presence.Status) pb.PresenceStatus {
	switch status {
	case presence.StatusOnline:
		return pb.PresenceStatus_ONLINE
	case presence.StatusIdle:
		return pb.PresenceStatus_IDLE
	case presence.StatusDoNotDisturb:
		return pb.PresenceStatus_DO_NOT_DISTURB
	case presence.StatusAway:
		return pb.PresenceStatus_AWAY
	case presence.StatusOffline:
		return pb.PresenceStatus_OFFLINE
	default:
		return pb.PresenceStatus_ONLINE
	}
}
