package auth

import (
	"context"
	"log/slog"
	"net/http"

	"github.com/google/uuid"
)

// TenantLookup resolves and verifies tenant access for a user.
// Defined as an interface to avoid circular dependency with services.
type TenantLookup interface {
	FirstTenantForUser(ctx context.Context, userID uuid.UUID) (*uuid.UUID, error)
	GetUserRole(ctx context.Context, userID, tenantID uuid.UUID) (string, error)
}

// TenantResolver is middleware that resolves the tenant from X-Tenant-ID header
// or defaults to the user's first tenant. Always verifies user has access.
type TenantResolver struct {
	lookup TenantLookup
}

func NewTenantResolver(lookup TenantLookup) *TenantResolver {
	return &TenantResolver{lookup: lookup}
}

func (tr *TenantResolver) Resolve(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		userID, ok := UserFromContext(r.Context())
		if !ok {
			http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
			return
		}

		var tenantID uuid.UUID
		ctx := r.Context()

		if header := r.Header.Get("X-Tenant-ID"); header != "" {
			parsed, err := uuid.Parse(header)
			if err != nil {
				http.Error(w, `{"error":"invalid X-Tenant-ID"}`, http.StatusBadRequest)
				return
			}

			// Verify user has access and get their role
			role, err := tr.lookup.GetUserRole(r.Context(), userID, parsed)
			if err != nil {
				slog.Error("tenant access check failed", "error", err, "user_id", userID, "tenant_id", parsed)
				http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError)
				return
			}
			if role == "" {
				http.Error(w, `{"error":"no access to tenant"}`, http.StatusForbidden)
				return
			}

			tenantID = parsed
			ctx = ContextWithUserRole(ctx, role)
		} else {
			// Default to user's first tenant
			first, err := tr.lookup.FirstTenantForUser(r.Context(), userID)
			if err != nil {
				slog.Error("failed to resolve default tenant", "error", err, "user_id", userID)
				http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError)
				return
			}
			if first == nil {
				http.Error(w, `{"error":"no tenant found for user"}`, http.StatusBadRequest)
				return
			}
			tenantID = *first

			// Look up role for default tenant
			role, err := tr.lookup.GetUserRole(r.Context(), userID, tenantID)
			if err != nil {
				slog.Error("failed to get user role", "error", err, "user_id", userID, "tenant_id", tenantID)
				http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError)
				return
			}
			ctx = ContextWithUserRole(ctx, role)
		}

		ctx = ContextWithTenantID(ctx, tenantID)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}
