package handlers

// v0.38.1: ConnectionResolver bridges the sandbox connections module
// to the store + vault layers. Implements sandbox.ConnectionResolver.

import (
	"context"
	"encoding/json"

	"switchboard-core/crypto"
	"switchboard-core/models"
	"switchboard-core/store"
)

// ConnectionResolverAdapter implements sandbox.ConnectionResolver.
type ConnectionResolverAdapter struct {
	stores store.Stores
	vault  *crypto.KeyResolver
}

func NewConnectionResolverAdapter(s store.Stores, vault *crypto.KeyResolver) *ConnectionResolverAdapter {
	return &ConnectionResolverAdapter{stores: s, vault: vault}
}

// Resolve returns a single connection with secrets decrypted.
func (r *ConnectionResolverAdapter) Resolve(ctx context.Context, userID, connType, name string) (*models.ExtConnection, error) {
	conn, err := r.stores.Connections.Resolve(ctx, userID, connType, name)
	if err != nil {
		return nil, err
	}
	r.decryptInPlace(ctx, conn)
	return conn, nil
}

// ResolveAll returns all accessible connections with secrets decrypted.
func (r *ConnectionResolverAdapter) ResolveAll(ctx context.Context, userID, connType string) ([]models.ExtConnection, error) {
	conns, err := r.stores.Connections.ResolveAll(ctx, userID, connType)
	if err != nil {
		return nil, err
	}
	for i := range conns {
		r.decryptInPlace(ctx, &conns[i])
	}
	return conns, nil
}

// decryptInPlace decrypts secret fields in the connection's config.
func (r *ConnectionResolverAdapter) decryptInPlace(ctx context.Context, conn *models.ExtConnection) {
	if conn == nil || conn.Config == nil || r.vault == nil {
		return
	}
	secretFields := r.lookupSecretFields(ctx, conn.PackageID, conn.Type)
	for k, v := range conn.Config {
		if !secretFields[k] {
			continue
		}
		m, ok := v.(map[string]interface{})
		if !ok {
			continue
		}
		enc := toBytes(m["enc"])
		nonce := toBytes(m["nonce"])
		if len(enc) == 0 {
			continue
		}
		plaintext := r.safeDecrypt(enc, nonce, conn.Scope, conn.OwnerID)
		if plaintext != "" {
			conn.Config[k] = plaintext
		}
	}
}

// safeDecrypt wraps vault.Decrypt to recover from GCM panics caused by
// invalid nonce lengths (e.g. stale vault keys after container restart).
func (r *ConnectionResolverAdapter) safeDecrypt(enc, nonce []byte, scope, ownerID string) (plaintext string) {
	defer func() {
		if rv := recover(); rv != nil {
			plaintext = ""
		}
	}()
	result, err := r.vault.Decrypt(enc, nonce, scope, ownerID)
	if err != nil {
		return ""
	}
	return result
}

// lookupSecretFields reads the package manifest to identify secret fields.
func (r *ConnectionResolverAdapter) lookupSecretFields(ctx context.Context, packageID, connType string) map[string]bool {
	result := map[string]bool{}
	pkg, err := r.stores.Packages.Get(ctx, packageID)
	if err != nil || pkg == nil {
		return result
	}
	connsRaw, ok := pkg.Manifest["connections"]
	if !ok {
		return result
	}
	connsJSON, _ := json.Marshal(connsRaw)
	var conns []struct {
		Type   string                       `json:"type"`
		Fields map[string]map[string]string `json:"fields"`
	}
	json.Unmarshal(connsJSON, &conns)
	for _, cd := range conns {
		if cd.Type == connType {
			for fieldName, fieldDef := range cd.Fields {
				if fieldDef["type"] == "secret" {
					result[fieldName] = true
				}
			}
			break
		}
	}
	return result
}
