package grpc

import (
	"context"

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

// ChannelServiceServer implements the ChannelService gRPC service
type ChannelServiceServer struct {
	pb.UnimplementedChannelServiceServer
	server *Server
}

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

// CreateChannel creates a new voice channel
func (c *ChannelServiceServer) CreateChannel(ctx context.Context, req *pb.CreateChannelRequest) (*pb.CreateChannelResponse, error) {
	// Extract user ID from token
	userID := extractUserIDFromContext(ctx)
	if userID == "" {
		return nil, ErrUnauthorized
	}

	if req.Name == "" {
		return nil, ErrInvalidChannelName
	}

	ch, err := c.server.channelManager.CreateChannel(req.Name, userID)
	if err != nil {
		if err == channel.ErrChannelAlreadyExists {
			return nil, ErrChannelAlreadyExists
		}
		if err == channel.ErrInvalidChannelName {
			return nil, ErrInvalidChannelName
		}
		return nil, err
	}

	return &pb.CreateChannelResponse{
		Status:  &pb.Status{Success: true},
		Channel: convertChannelToProto(ch),
	}, nil
}

// GetChannel retrieves a channel by ID
func (c *ChannelServiceServer) GetChannel(ctx context.Context, req *pb.GetChannelRequest) (*pb.Channel, error) {
	if req.ChannelId == "" {
		return nil, ErrInvalidChannel
	}

	ch, err := c.server.channelManager.GetChannel(req.ChannelId)
	if err != nil {
		if err == channel.ErrChannelNotFound {
			return nil, ErrChannelNotFound
		}
		return nil, err
	}

	return convertChannelToProto(ch), nil
}

// ListChannels lists all active channels
func (c *ChannelServiceServer) ListChannels(ctx context.Context, req *pb.ListChannelsRequest) (*pb.ListChannelsResponse, error) {
	channels := c.server.channelManager.ListChannels()

	pbChannels := make([]*pb.Channel, 0, len(channels))
	for _, ch := range channels {
		pbChannels = append(pbChannels, convertChannelToProto(ch))
	}

	return &pb.ListChannelsResponse{
		Channels: pbChannels,
	}, nil
}

// UpdateChannel updates channel properties
func (c *ChannelServiceServer) UpdateChannel(ctx context.Context, req *pb.UpdateChannelRequest) (*pb.Channel, error) {
	if req.ChannelId == "" {
		return nil, ErrInvalidChannel
	}

	ch, err := c.server.channelManager.UpdateChannel(
		req.ChannelId,
		req.Name,
		req.Description,
		req.IsPublic,
		req.MaxUsers,
	)
	if err != nil {
		if err == channel.ErrChannelNotFound {
			return nil, ErrChannelNotFound
		}
		if err == channel.ErrChannelAlreadyExists {
			return nil, ErrChannelAlreadyExists
		}
		return nil, err
	}

	return convertChannelToProto(ch), nil
}

// DeleteChannel deletes a channel
func (c *ChannelServiceServer) DeleteChannel(ctx context.Context, req *pb.DeleteChannelRequest) (*pb.Status, error) {
	if req.ChannelId == "" {
		return nil, ErrInvalidChannel
	}

	err := c.server.channelManager.DeleteChannel(req.ChannelId, req.HardDelete)
	if err != nil {
		if err == channel.ErrChannelNotFound {
			return nil, ErrChannelNotFound
		}
		return nil, err
	}

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

// JoinChannel adds the user to a channel
func (c *ChannelServiceServer) JoinChannel(ctx context.Context, req *pb.JoinChannelRequest) (*pb.JoinChannelResponse, error) {
	userID := extractUserIDFromContext(ctx)
	if userID == "" {
		return nil, ErrUnauthorized
	}

	if req.ChannelId == "" {
		return nil, ErrInvalidChannel
	}

	ch, err := c.server.channelManager.JoinChannel(req.ChannelId, userID)
	if err != nil {
		if err == channel.ErrChannelNotFound {
			return nil, ErrChannelNotFound
		}
		if err == channel.ErrChannelFull {
			return nil, ErrChannelFull
		}
		return nil, err
	}

	// Create presence session
	c.server.presenceManager.CreateSession(userID)
	c.server.presenceManager.SetChannelPresence(userID, req.ChannelId)

	return &pb.JoinChannelResponse{
		Status:  &pb.Status{Success: true},
		Channel: convertChannelToProto(ch),
	}, nil
}

// LeaveChannel removes the user from a channel
func (c *ChannelServiceServer) LeaveChannel(ctx context.Context, req *pb.LeaveChannelRequest) (*pb.Status, error) {
	userID := extractUserIDFromContext(ctx)
	if userID == "" {
		return nil, ErrUnauthorized
	}

	if req.ChannelId == "" {
		return nil, ErrInvalidChannel
	}

	err := c.server.channelManager.LeaveChannel(req.ChannelId, userID)
	if err != nil {
		if err == channel.ErrChannelNotFound {
			return nil, ErrChannelNotFound
		}
		if err == channel.ErrUserNotInChannel {
			return nil, ErrUserNotInChannel
		}
		return nil, err
	}

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

// ListMembers lists members of a channel
func (c *ChannelServiceServer) ListMembers(ctx context.Context, req *pb.ListMembersRequest) (*pb.ListMembersResponse, error) {
	if req.ChannelId == "" {
		return nil, ErrInvalidChannel
	}

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

	return &pb.ListMembersResponse{
		MemberIds: members,
	}, nil
}

// SubscribeChannelEvents subscribes to channel events
func (c *ChannelServiceServer) SubscribeChannelEvents(req *pb.SubscribeChannelEventsRequest, stream pb.ChannelService_SubscribeChannelEventsServer) error {
	// This is a streaming endpoint - would need to implement a proper event system
	// For now, return not implemented
	return ErrNotImplemented
}

// convertChannelToProto converts internal channel to proto format
func convertChannelToProto(ch *channel.Channel) *pb.Channel {
	return &pb.Channel{
		Id:          ch.ID,
		Name:        ch.Name,
		Description: ch.Description,
		IsPublic:    ch.IsPublic,
		OwnerId:     ch.OwnerID,
		MemberIds:   ch.GetMembers(),
		MaxUsers:    ch.MaxUsers,
		CreatedAt:   ch.CreatedAt.Unix(),
		UpdatedAt:   ch.UpdatedAt.Unix(),
	}
}

// extractUserIDFromContext extracts user ID from context
func extractUserIDFromContext(ctx context.Context) string {
	// In a real implementation, extract from JWT claims
	// For MVP, use a default user ID
	if userID, ok := ctx.Value("userID").(string); ok {
		return userID
	}
	return "default-user"
}
