package permissions

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"strings"
	"time"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/rs/zerolog/log"
)

const permissionReadOperationEffectivePermissions = "effective_permissions"

type permissionReadPhase struct {
	ctx         context.Context
	correlation string
	operation   string
	phase       string
	targetScope string
	startedAt   time.Time
}

func startPermissionReadPhase(ctx context.Context, operation, phase, targetScope string) *permissionReadPhase {
	tracker := &permissionReadPhase{
		ctx:         ctx,
		correlation: permissionReadCorrelation(ctx),
		operation:   operation,
		phase:       phase,
		targetScope: targetScope,
		startedAt:   time.Now(),
	}
	log.Info().
		Str("event", "permission_read_phase").
		Str("correlation", tracker.correlation).
		Str("operation", tracker.operation).
		Str("phase", tracker.phase).
		Str("state", "start").
		Str("target_scope", tracker.targetScope).
		Msg("Permission read phase")
	return tracker
}

func (p *permissionReadPhase) complete(rows int, err error) {
	p.completeWithOutcome(rows, permissionReadOutcome(p.ctx, err))
}

func (p *permissionReadPhase) completeWithOutcome(rows int, outcome string) {
	event := log.Info().
		Str("event", "permission_read_phase").
		Str("correlation", p.correlation).
		Str("operation", p.operation).
		Str("phase", p.phase).
		Str("state", "complete").
		Str("outcome", outcome).
		Str("target_scope", p.targetScope).
		Int("rows", rows).
		Int64("elapsed_ms", time.Since(p.startedAt).Milliseconds())
	event.Msg("Permission read phase")
}

func runPermissionReadPhase[T any](
	ctx context.Context,
	operation string,
	phase string,
	targetScope string,
	rowCount func(T) int,
	load func() (T, error),
) (value T, err error) {
	tracker := startPermissionReadPhase(ctx, operation, phase, targetScope)
	defer func() {
		if recovered := recover(); recovered != nil {
			tracker.completeWithOutcome(0, "panic")
			panic(recovered)
		}
		rows := 0
		if rowCount != nil {
			rows = rowCount(value)
		}
		tracker.complete(rows, err)
	}()
	return load()
}

func permissionReadCorrelation(ctx context.Context) string {
	requestID, _ := ctx.Value(constants.ContextKeyRequestID).(string)
	requestID = strings.TrimSpace(requestID)
	if requestID == "" {
		return ""
	}
	if len(requestID) > 128 {
		requestID = requestID[:128]
	}
	sum := sha256.Sum256([]byte(requestID))
	return hex.EncodeToString(sum[:8])
}

func permissionReadOutcome(ctx context.Context, err error) string {
	if err == nil {
		return "ok"
	}
	if errors.Is(ctx.Err(), context.DeadlineExceeded) || errors.Is(err, context.DeadlineExceeded) {
		return "deadline"
	}
	if errors.Is(ctx.Err(), context.Canceled) || errors.Is(err, context.Canceled) {
		return "canceled"
	}
	return "error"
}
