package service

import (
	"context"
	"errors"
	"testing"

	"github.com/owncord/server/db"
	"github.com/owncord/server/permissions"
)

// PermissionService.RequireChannelAccess had no coverage. It is the single
// authorization gate that splits DM channels (membership check) from regular
// channels (role + override check); a wrong branch here either leaks a DM to a
// non-participant or locks legitimate users out of a channel.

func TestRequireChannelAccess_RegularChannel(t *testing.T) {
	svc, database := newTestPermService(t)
	seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
	ctx := context.Background()

	// The seeded Member role has SendMessages but not ManageChannels.
	if err := svc.RequireChannelAccess(ctx, 1, "text", 10, permissions.SendMessages); err != nil {
		t.Errorf("RequireChannelAccess with a granted permission = %v, want nil", err)
	}

	err := svc.RequireChannelAccess(ctx, 1, "text", 10, permissions.ManageChannels)
	if !errors.Is(err, permissions.ErrPermissionDenied) {
		t.Errorf("RequireChannelAccess with a missing permission = %v, want ErrPermissionDenied", err)
	}
}

func TestRequireChannelAccess_DMParticipant(t *testing.T) {
	svc, database := newTestPermService(t)
	seedChannel(t, database, &db.Channel{ID: 20, Name: "dm", Type: "dm"})
	seedDMParticipant(t, database, 20, 1)
	ctx := context.Background()

	// For a DM the permission argument is irrelevant — membership decides.
	// Pass a permission the Member role does not hold to prove that.
	if err := svc.RequireChannelAccess(ctx, 1, "dm", 20, permissions.ManageChannels); err != nil {
		t.Errorf("RequireChannelAccess for a DM participant = %v, want nil", err)
	}
}

func TestRequireChannelAccess_DMNonParticipant(t *testing.T) {
	svc, database := newTestPermService(t)
	seedChannel(t, database, &db.Channel{ID: 20, Name: "dm", Type: "dm"})
	seedUserRole(t, database, 2, permissions.MemberRoleID)
	seedDMParticipant(t, database, 20, 1)
	ctx := context.Background()

	// User 2 is not in the DM. Role permissions must not open it.
	err := svc.RequireChannelAccess(ctx, 2, "dm", 20, permissions.ReadMessages)
	if !errors.Is(err, permissions.ErrNotDMParticipant) {
		t.Errorf("RequireChannelAccess for a non-participant = %v, want ErrNotDMParticipant", err)
	}
}

func TestRequireChannelAccess_DMWithNoParticipantsDenies(t *testing.T) {
	svc, database := newTestPermService(t)
	seedChannel(t, database, &db.Channel{ID: 21, Name: "empty-dm", Type: "dm"})
	ctx := context.Background()

	err := svc.RequireChannelAccess(ctx, 1, "dm", 21, permissions.ReadMessages)
	if !errors.Is(err, permissions.ErrNotDMParticipant) {
		t.Errorf("RequireChannelAccess on a DM with no participants = %v, want ErrNotDMParticipant", err)
	}
}

func TestRequireChannelAccess_UnknownUserDenied(t *testing.T) {
	svc, database := newTestPermService(t)
	seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
	ctx := context.Background()

	// A user with no row has no role, so getOrPopulate yields nothing and the
	// check must fail closed.
	err := svc.RequireChannelAccess(ctx, 9999, "text", 10, permissions.ReadMessages)
	if !errors.Is(err, permissions.ErrPermissionDenied) {
		t.Errorf("RequireChannelAccess for an unknown user = %v, want ErrPermissionDenied", err)
	}
}
