package pusher

import (
	"context"
	"errors"
	"strings"

	"github.com/flowy-live/llink/internal/network"
)

var ErrUnauthorized = errors.New("unauthorized")

type Authorizer struct {
	networkReader network.Reader
}

func NewAuthorizer(networkReader network.Reader) *Authorizer {
	return &Authorizer{networkReader: networkReader}
}

// Authorize accepts channel IDs of the form:
//
//	network:{networkId}
//	stream:{networkId}:{streamId}
//	_presence:{humanId}
func (a *Authorizer) Authorize(ctx context.Context, channelID, humanID string) error {
	parts := strings.SplitN(channelID, ":", 2)
	if len(parts) < 2 {
		return ErrUnauthorized
	}

	channelType := parts[0]
	rest := parts[1]

	switch channelType {
	case "network":
		return a.authorizeNetwork(ctx, rest, humanID)
	case "stream":
		return a.authorizeStream(ctx, rest, humanID)
	case "_presence":
		// Only the owning human may subscribe to their presence channel.
		if rest != humanID {
			return ErrUnauthorized
		}
		return nil
	default:
		return ErrUnauthorized
	}
}

func (a *Authorizer) authorizeNetwork(ctx context.Context, networkID, humanID string) error {
	isMember, err := a.networkReader.IsMember(ctx, networkID, humanID)
	if err != nil {
		return err
	}
	if !isMember {
		return ErrUnauthorized
	}
	return nil
}

// rest is "{networkId}:{streamId}"; stream-level visibility is enforced by network access.
func (a *Authorizer) authorizeStream(ctx context.Context, rest, humanID string) error {
	parts := strings.SplitN(rest, ":", 2)
	if len(parts) < 2 {
		return ErrUnauthorized
	}
	networkID := parts[0]
	return a.authorizeNetwork(ctx, networkID, humanID)
}
