package auth

import (
	"context"
	"crypto/sha256"
	"crypto/x509"
	"encoding/hex"
	"fmt"
	"log"
	"strings"

	"armature/models"
	"armature/store"
)

// ParseDN parses an RFC 2253 / RFC 4514 distinguished name into key-value pairs.
//
// Examples:
//
//	"CN=Jeff Smith,O=Acme Corp,OU=Engineering"
//	"CN=Jane Doe,emailAddress=jane@acme.com,O=Acme Corp"
//
// Handles simple comma-separated key=value pairs. Does NOT handle
// escaped commas in values (\,) or multi-valued RDNs (+). Sufficient
// for typical X.509 client cert DNs.
func ParseDN(dn string) map[string]string {
	result := make(map[string]string)
	parts := strings.Split(dn, ",")
	for _, part := range parts {
		part = strings.TrimSpace(part)
		idx := strings.Index(part, "=")
		if idx < 1 {
			continue
		}
		key := strings.TrimSpace(part[:idx])
		val := strings.TrimSpace(part[idx+1:])
		result[key] = val
	}
	return result
}

// FingerprintCert returns the hex-encoded SHA-256 hash of a certificate's
// raw DER encoding. This is used as the stable external_id for mTLS users.
func FingerprintCert(cert *x509.Certificate) string {
	h := sha256.Sum256(cert.Raw)
	return hex.EncodeToString(h[:])
}

// resolveOrProvision looks up an existing user by auth_source=mtls and external_id,
// or auto-provisions a new user from the certificate's CN and fingerprint.
//
// Shared by both MTLSProxyProvider and MTLSNativeProvider.
func resolveOrProvision(
	ctx context.Context,
	stores store.Stores,
	cn string,
	dnFields map[string]string,
	fingerprint string,
	autoActivate bool,
	defaultTeam string,
) (*Result, error) {
	// ── Look up existing user ──────────────────────────────────────
	user, err := stores.Users.GetByExternalID(ctx, string(ModeMTLS), fingerprint)
	if err == nil && user != nil {
		if !user.IsActive {
			return nil, ErrInactive
		}
		log.Printf("[auth/mtls] existing user %s (%s)", user.Username, cn)
		return &Result{User: user, IsNewUser: false, VaultHint: ""}, nil
	}

	// ── Auto-provision ─────────────────────────────────────────────
	if !autoActivate {
		return nil, fmt.Errorf("%w: auto-provision disabled", ErrInvalidCreds)
	}

	email := dnFields["emailAddress"]
	if email == "" {
		email = models.HandleFromName(cn) + "@mtls.local"
	}

	handle := UniqueHandle(ctx, stores.Users, models.HandleFromName(cn))

	user = &models.User{
		Username:    handle,
		Email:       email,
		DisplayName: cn,
		IsActive:    true,
		AuthSource:  string(ModeMTLS),
		ExternalID:  &fingerprint,
		Handle:      handle,
	}

	if err := stores.Users.Create(ctx, user); err != nil {
		return nil, fmt.Errorf("auto-provision failed: %w", err)
	}

	log.Printf("[auth/mtls] auto-provisioned user %s from CN=%s", user.ID, cn)
	EnsureEveryoneGroup(ctx, stores, user.ID)

	if defaultTeam != "" {
		if err := stores.Teams.AddMember(ctx, defaultTeam, user.ID, "member"); err != nil {
			log.Printf("[auth/mtls] warn: could not add %s to default team: %v", user.ID, err)
		}
	}

	return &Result{User: user, IsNewUser: true, VaultHint: ""}, nil
}
