package organization

import (
	"context"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
)

type organizationMembershipGate struct {
	MemberID string
	Role     string
}

type queryRower interface {
	QueryRow(context.Context, string, ...any) coredb.Row
}

type queryExecerRower interface {
	coredb.QueryExecer
	QueryRow(context.Context, string, ...any) coredb.Row
}

func (txs TxStore) requireActiveOrganizationMembership(ctx context.Context, queryer queryRower, userID string, organizationID string) (organizationMembershipGate, error) {
	row := queryer.QueryRow(ctx, `
SELECT om.id, om.org_role
FROM organization_members om
JOIN organization_settings os ON os.organization_id = om.organization_id
WHERE om.organization_id = $1
  AND om.user_id = $2
  AND om.status = 'active'
  AND os.status = 'active'
`, organizationID, userID)

	var membership organizationMembershipGate
	if err := row.Scan(&membership.MemberID, &membership.Role); err != nil {
		if coredb.IsNotFound(err) {
			return organizationMembershipGate{}, ErrForbidden
		}
		return organizationMembershipGate{}, fmt.Errorf("load organization membership: %w", err)
	}
	return membership, nil
}

func (txs TxStore) requireActiveOrganizationAdmin(ctx context.Context, queryer queryRower, userID string, organizationID string) (organizationMembershipGate, error) {
	membership, err := txs.requireActiveOrganizationMembership(ctx, queryer, userID, organizationID)
	if err != nil {
		return organizationMembershipGate{}, err
	}
	if membership.Role != organizationRoleOwner && membership.Role != organizationRoleAdmin {
		return organizationMembershipGate{}, ErrForbidden
	}
	return membership, nil
}

// RequireOrganizationAdmin verifies one exact user/organization pair without
// loading the caller's full organization collection.
func (store *Store) RequireOrganizationAdmin(ctx context.Context, userID string, organizationID string) error {
	if store == nil || store.db == nil {
		return fmt.Errorf("organization store is not configured")
	}
	return coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		return store.InTx(tx).RequireOrganizationAdmin(ctx, userID, organizationID)
	})
}

func (txs TxStore) RequireOrganizationAdmin(ctx context.Context, userID string, organizationID string) error {
	_, err := txs.requireActiveOrganizationAdmin(
		ctx,
		txs.db,
		strings.TrimSpace(userID),
		strings.TrimSpace(organizationID),
	)
	return err
}

func (txs TxStore) requireOrganizationMemberManagement(ctx context.Context, userID string, organizationID string, accountID string) error {
	membership, err := txs.requireActiveOrganizationMembership(ctx, txs.db, userID, organizationID)
	if err != nil {
		return err
	}
	if membership.Role == organizationRoleOwner || membership.Role == organizationRoleAdmin || membership.MemberID == accountID {
		return nil
	}
	return ErrForbidden
}

func (txs TxStore) requireOrganizationWorkspaceBinding(ctx context.Context, queryer queryRower, organizationID string, workspaceID string) error {
	row := queryer.QueryRow(ctx, `
SELECT 1
FROM workspaces
WHERE organization_id = $1 AND id = $2
`, organizationID, workspaceID)
	var exists int
	if err := row.Scan(&exists); err != nil {
		if coredb.IsNotFound(err) {
			return ErrForbidden
		}
		return fmt.Errorf("load organization workspace ownership: %w", err)
	}
	return nil
}

func (txs TxStore) requireActiveOrganizationWorkspaceGrant(ctx context.Context, queryer queryer, organizationID string, workspaceID string, accountID string) error {
	_, hasAccess, err := txs.loadEffectiveOrganizationWorkspaceRole(ctx, queryer, organizationID, workspaceID, accountID)
	if err != nil {
		return err
	}
	if !hasAccess {
		return ErrForbidden
	}
	return nil
}

func (txs TxStore) requireActiveOrganizationProviderGrant(ctx context.Context, queryer queryRower, organizationID string, workspaceID string, providerID string) error {
	trimmedProviderID := strings.TrimSpace(providerID)
	if trimmedProviderID == "" {
		return nil
	}
	visibleProviders, err := txs.ListVisibleOrganizationProvidersForWorkspace(ctx, workspaceID)
	if err != nil {
		return err
	}
	if agent.IsAgentTeamProxyProviderID(trimmedProviderID) {
		if len(visibleProviders) == 0 {
			return ErrForbidden
		}
		return nil
	}
	for _, provider := range visibleProviders {
		if strings.TrimSpace(provider.OrganizationID) != strings.TrimSpace(organizationID) {
			continue
		}
		if strings.TrimSpace(provider.ID) == trimmedProviderID {
			return nil
		}
	}
	return ErrForbidden
}

func resolveOrganizationWorkspaceRole(requestedRole string, fallbackRole string) (string, error) {
	resolvedRole := strings.TrimSpace(requestedRole)
	if resolvedRole == "" {
		resolvedRole = strings.TrimSpace(fallbackRole)
	}
	if resolvedRole == "" {
		resolvedRole = string(domain.WorkspaceRoleMember)
	}

	switch domain.WorkspaceRole(resolvedRole) {
	case domain.WorkspaceRoleOwner, domain.WorkspaceRoleAdmin, domain.WorkspaceRoleMember:
		return resolvedRole, nil
	default:
		return "", badRequest("role must be owner, admin, or member")
	}
}
