package discovery

import (
	"context"
	"fmt"
	"time"

	grpcclient "github.com/rwadurian/mpc-system/services/session-coordinator/adapters/output/grpc"
	"github.com/rwadurian/mpc-system/services/session-coordinator/application/ports/output"
	"go.uber.org/zap"
)

// MessageRouterPartyDiscovery implements PartyPoolPort using Message Router
// This replaces K8s-based discovery with a decentralized approach
type MessageRouterPartyDiscovery struct {
	client *grpcclient.MessageRouterClient
	logger *zap.Logger
}

// NewMessageRouterPartyDiscovery creates a new Message Router-based party discovery
func NewMessageRouterPartyDiscovery(client *grpcclient.MessageRouterClient, logger *zap.Logger) *MessageRouterPartyDiscovery {
	return &MessageRouterPartyDiscovery{
		client: client,
		logger: logger,
	}
}

// GetAvailableParties returns all available party endpoints
// Implements output.PartyPoolPort interface
func (d *MessageRouterPartyDiscovery) GetAvailableParties() []output.PartyEndpoint {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	parties, err := d.client.GetRegisteredParties(ctx, "")
	if err != nil {
		d.logger.Error("Failed to get available parties from Message Router", zap.Error(err))
		return []output.PartyEndpoint{}
	}

	endpoints := make([]output.PartyEndpoint, 0, len(parties))
	for _, party := range parties {
		endpoint := output.PartyEndpoint{
			PartyID: party.PartyId,
			Ready:   party.Online,
			Role:    output.PartyRole(party.Role),
		}

		// Parse notification channel from party registration
		// If notification channel is present, party operates in offline mode
		// If not present, party operates in real-time mode (Message Router push)
		if party.Notification != nil {
			endpoint.Notification = &output.NotificationChannel{
				Email:     party.Notification.Email,
				Phone:     party.Notification.Phone,
				PushToken: party.Notification.PushToken,
			}
		}

		endpoints = append(endpoints, endpoint)
	}

	return endpoints
}

// GetAvailablePartiesByRole returns available parties filtered by role
// Implements output.PartyPoolPort interface
func (d *MessageRouterPartyDiscovery) GetAvailablePartiesByRole(role output.PartyRole) []output.PartyEndpoint {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	parties, err := d.client.GetRegisteredParties(ctx, string(role))
	if err != nil {
		d.logger.Error("Failed to get parties by role from Message Router",
			zap.String("role", string(role)),
			zap.Error(err))
		return []output.PartyEndpoint{}
	}

	endpoints := make([]output.PartyEndpoint, 0, len(parties))
	for _, party := range parties {
		endpoint := output.PartyEndpoint{
			PartyID: party.PartyId,
			Ready:   party.Online,
			Role:    output.PartyRole(party.Role),
		}

		// Parse notification channel from party registration
		// If notification channel is present, party operates in offline mode
		// If not present, party operates in real-time mode (Message Router push)
		if party.Notification != nil {
			endpoint.Notification = &output.NotificationChannel{
				Email:     party.Notification.Email,
				Phone:     party.Notification.Phone,
				PushToken: party.Notification.PushToken,
			}
		}

		endpoints = append(endpoints, endpoint)
	}

	return endpoints
}

// SelectParties randomly selects n parties from the available pool
// Implements output.PartyPoolPort interface
func (d *MessageRouterPartyDiscovery) SelectParties(n int) ([]output.PartyEndpoint, error) {
	available := d.GetAvailableParties()

	if len(available) < n {
		return nil, fmt.Errorf("insufficient parties: need %d, have %d", n, len(available))
	}

	// For now, return first n parties
	// TODO: Implement random selection or load balancing strategy
	selected := make([]output.PartyEndpoint, n)
	copy(selected, available[:n])

	return selected, nil
}

// SelectPartiesWithFilter selects parties based on filter criteria
// Implements output.PartyPoolPort interface
func (d *MessageRouterPartyDiscovery) SelectPartiesWithFilter(filter output.PartySelectionFilter) ([]output.PartyEndpoint, error) {
	var available []output.PartyEndpoint

	// If role is specified, filter by role; otherwise get all available parties
	if filter.Role != "" {
		available = d.GetAvailablePartiesByRole(filter.Role)
	} else {
		available = d.GetAvailableParties()
	}

	if len(available) < filter.Count {
		return nil, fmt.Errorf("insufficient parties with role %s: need %d, have %d", filter.Role, filter.Count, len(available))
	}

	// For now, return first n parties
	// TODO: Implement random selection or load balancing strategy
	selected := make([]output.PartyEndpoint, filter.Count)
	copy(selected, available[:filter.Count])

	d.logger.Info("Selected parties from Message Router",
		zap.String("role", string(filter.Role)),
		zap.Int("requested", filter.Count),
		zap.Int("selected", len(selected)))

	return selected, nil
}
