package client

import (
	"context"
	"fmt"

	pb "github.com/sorti/openspeak/pkg/api/openspeak/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/metadata"
)

// GRPCClient wraps gRPC service clients
type GRPCClient struct {
	conn       *grpc.ClientConn
	AuthClient pb.AuthServiceClient
	ChannelClient pb.ChannelServiceClient
	PresenceClient pb.PresenceServiceClient
	VoiceClient pb.VoiceServiceClient
	Token      string
}

// NewGRPCClient creates a new gRPC client connection
func NewGRPCClient(host string, port int) (*GRPCClient, error) {
	addr := fmt.Sprintf("%s:%d", host, port)

	conn, err := grpc.Dial(
		addr,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to gRPC server: %w", err)
	}

	return &GRPCClient{
		conn:           conn,
		AuthClient:     pb.NewAuthServiceClient(conn),
		ChannelClient:  pb.NewChannelServiceClient(conn),
		PresenceClient: pb.NewPresenceServiceClient(conn),
		VoiceClient:    pb.NewVoiceServiceClient(conn),
	}, nil
}

// Close closes the gRPC connection
func (c *GRPCClient) Close() error {
	if c.conn != nil {
		return c.conn.Close()
	}
	return nil
}

// ContextWithToken returns a context with the auth token in metadata
func (c *GRPCClient) ContextWithToken(ctx context.Context) context.Context {
	if c.Token == "" {
		return ctx
	}
	return metadata.AppendToOutgoingContext(ctx, "authorization", c.Token)
}

// Login authenticates with the server using a token
func (c *GRPCClient) Login(ctx context.Context, token string) (*pb.LoginResponse, error) {
	req := &pb.LoginRequest{Token: token}
	resp, err := c.AuthClient.Login(ctx, req)
	if err != nil {
		return nil, err
	}

	// Store token for future requests
	c.Token = token
	return resp, nil
}

// ValidateToken validates a token
func (c *GRPCClient) ValidateToken(ctx context.Context, token string) (*pb.ValidateTokenResponse, error) {
	req := &pb.ValidateTokenRequest{Token: token}
	return c.AuthClient.ValidateToken(ctx, req)
}

// GetMyPermissions retrieves user permissions
func (c *GRPCClient) GetMyPermissions(ctx context.Context) (*pb.GetMyPermissionsResponse, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.GetMyPermissionsRequest{}
	return c.AuthClient.GetMyPermissions(ctx, req)
}

// CreateChannel creates a new channel
func (c *GRPCClient) CreateChannel(ctx context.Context, name, description string, isPublic bool, maxUsers int32) (*pb.CreateChannelResponse, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.CreateChannelRequest{
		Name:        name,
		Description: description,
		IsPublic:    isPublic,
		MaxUsers:    maxUsers,
	}
	return c.ChannelClient.CreateChannel(ctx, req)
}

// ListChannels lists all channels
func (c *GRPCClient) ListChannels(ctx context.Context) (*pb.ListChannelsResponse, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.ListChannelsRequest{}
	return c.ChannelClient.ListChannels(ctx, req)
}

// GetChannel retrieves a specific channel
func (c *GRPCClient) GetChannel(ctx context.Context, channelID string) (*pb.Channel, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.GetChannelRequest{ChannelId: channelID}
	return c.ChannelClient.GetChannel(ctx, req)
}

// JoinChannel joins a channel
func (c *GRPCClient) JoinChannel(ctx context.Context, channelID string) (*pb.JoinChannelResponse, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.JoinChannelRequest{ChannelId: channelID}
	return c.ChannelClient.JoinChannel(ctx, req)
}

// LeaveChannel leaves a channel
func (c *GRPCClient) LeaveChannel(ctx context.Context, channelID string) (*pb.Status, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.LeaveChannelRequest{ChannelId: channelID}
	return c.ChannelClient.LeaveChannel(ctx, req)
}

// ListMembers lists members of a channel
func (c *GRPCClient) ListMembers(ctx context.Context, channelID string) (*pb.ListMembersResponse, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.ListMembersRequest{ChannelId: channelID}
	return c.ChannelClient.ListMembers(ctx, req)
}

// GetMyPresence retrieves current user presence
func (c *GRPCClient) GetMyPresence(ctx context.Context) (*pb.UserPresence, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.GetPresenceRequest{}
	return c.PresenceClient.GetMyPresence(ctx, req)
}

// GetUserPresence retrieves another user's presence
func (c *GRPCClient) GetUserPresence(ctx context.Context, userID string) (*pb.UserPresence, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.GetPresenceRequest{UserId: userID}
	return c.PresenceClient.GetUserPresence(ctx, req)
}

// SetPresenceStatus updates presence status
func (c *GRPCClient) SetPresenceStatus(ctx context.Context, status pb.PresenceStatus) (*pb.UserPresence, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.SetPresenceStatusRequest{Status: status}
	return c.PresenceClient.SetPresenceStatus(ctx, req)
}

// SetMuteStatus updates mute status
func (c *GRPCClient) SetMuteStatus(ctx context.Context, micMuted, speakerMuted bool) (*pb.UserPresence, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.SetMuteStatusRequest{
		MicrophoneMuted: micMuted,
		SpeakerMuted:    speakerMuted,
	}
	return c.PresenceClient.SetMuteStatus(ctx, req)
}

// ReportActivity reports user activity
func (c *GRPCClient) ReportActivity(ctx context.Context) (*pb.Status, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.ReportActivityRequest{}
	return c.PresenceClient.ReportActivity(ctx, req)
}

// ListChannelMembers lists members with presence info
func (c *GRPCClient) ListChannelMembers(ctx context.Context, channelID string) (*pb.ListChannelMembersResponse, error) {
	ctx = c.ContextWithToken(ctx)
	req := &pb.ListChannelMembersRequest{ChannelId: channelID}
	return c.PresenceClient.ListChannelMembers(ctx, req)
}
