package vault

import (
	"context"
	"net/http"
	"time"

	sdklogical "github.com/openbao/openbao/sdk/v2/logical"

	"github.com/stephnangue/warden/framework"
	"github.com/stephnangue/warden/logical"
)

// pathConfig returns the config path definition for the Vault provider
func (b *vaultBackend) pathConfig() *framework.Path {
	return &framework.Path{
		Pattern: "config",
		Fields: map[string]*framework.FieldSchema{
			"vault_address": {
				Type:        framework.TypeString,
				Description: "The address of the Vault server (e.g., https://vault.example.com:8200)",
				Required:    true,
			},
			"max_body_size": {
				Type:        framework.TypeInt64,
				Description: "Maximum request body size in bytes (default: 10MB, max: 100MB)",
				Default:     framework.DefaultMaxBodySize,
			},
			"timeout": {
				Type:        framework.TypeDurationSecond,
				Description: "Request timeout duration (e.g., '30s', '5m')",
				Default:     "30s",
			},
			"tls_skip_verify": {
				Type:        framework.TypeBool,
				Description: "Skip TLS certificate verification (not recommended for production)",
				Default:     false,
			},
			"ca_data": {
				Type:        framework.TypeString,
				Description: "Base64-encoded PEM CA certificate for custom/self-signed CAs",
			},
			"auto_auth_path": {
				Type:        framework.TypeString,
				Description: "Path to auth mount for implicit authentication (e.g., 'auth/jwt/', 'auth/cert/')",
			},
			"default_role": {
				Type:        framework.TypeString,
				Description: "Default role to use when not specified in URL path",
			},
		},
		Operations: map[logical.Operation]framework.OperationHandler{
			logical.ReadOperation: &framework.PathOperation{
				Callback: b.handleConfigRead,
				Summary:  "Read Vault provider configuration",
			},
			logical.UpdateOperation: &framework.PathOperation{
				Callback: b.handleConfigWrite,
				Summary:  "Configure Vault provider settings",
			},
		},
		HelpSynopsis:    "Configure Vault provider",
		HelpDescription: "This endpoint configures the Vault provider settings including server address, body size limits, and timeouts.",
	}
}

// handleConfigRead handles reading the Vault provider configuration
func (b *vaultBackend) handleConfigRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	tc := b.TransparentConfig
	return &logical.Response{
		StatusCode: http.StatusOK,
		Data: map[string]any{
			"vault_address":   b.vaultAddress,
			"max_body_size":   b.MaxBodySize,
			"timeout":         b.Timeout.String(),
			"tls_skip_verify": b.tlsSkipVerify,
			"ca_data":         b.caData,
			"auto_auth_path":  tc.AutoAuthPath,
			"default_role":    tc.DefaultAuthRole,
		},
	}, nil
}

// handleConfigWrite handles writing the Vault provider configuration
func (b *vaultBackend) handleConfigWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	// Apply values from request - framework already handles type conversion
	if val, ok := d.GetOk("vault_address"); ok {
		addr := val.(string)
		if err := validateVaultAddress(addr); err != nil {
			return &logical.Response{
				StatusCode: http.StatusBadRequest,
				Err:        logical.ErrBadRequest(err.Error()),
			}, nil
		}
		b.vaultAddress = addr
	}

	// For max_body_size: use provided value, or apply default if not yet set
	if val, ok := d.GetOk("max_body_size"); ok {
		b.MaxBodySize = val.(int64)
	} else if b.MaxBodySize == 0 {
		b.MaxBodySize = framework.DefaultMaxBodySize
	}

	// For timeout: use provided value, or apply default if not yet set
	if val, ok := d.GetOk("timeout"); ok {
		// TypeDurationSecond returns int (seconds)
		b.Timeout = time.Duration(val.(int)) * time.Second
	} else if b.Timeout == 0 {
		b.Timeout = framework.DefaultTimeout
	}

	tlsChanged := false
	if val, ok := d.GetOk("tls_skip_verify"); ok {
		newVal := val.(bool)
		if b.tlsSkipVerify != newVal {
			tlsChanged = true
		}
		b.tlsSkipVerify = newVal
	}
	if val, ok := d.GetOk("ca_data"); ok {
		newVal := val.(string)
		if b.caData != newVal {
			tlsChanged = true
		}
		b.caData = newVal
	}

	// Update transport if TLS settings changed
	if tlsChanged {
		transport, err := newVaultTransport(b.caData, b.tlsSkipVerify)
		if err != nil {
			return &logical.Response{
				StatusCode: http.StatusBadRequest,
				Err:        logical.ErrBadRequest(err.Error()),
			}, nil
		}
		b.Proxy.Transport = transport
	}

	// Transparent mode settings — build new config from current values + overrides
	tc := &framework.TransparentConfig{
		AutoAuthPath:    b.TransparentConfig.AutoAuthPath,
		DefaultAuthRole: b.TransparentConfig.DefaultAuthRole,
	}
	if val, ok := d.GetOk("auto_auth_path"); ok {
		tc.AutoAuthPath = val.(string)
	}
	if val, ok := d.GetOk("default_role"); ok {
		tc.DefaultAuthRole = val.(string)
	}

	// Validate: auto_auth_path required
	if tc.AutoAuthPath == "" {
		return &logical.Response{
			StatusCode: http.StatusBadRequest,
			Err:        logical.ErrBadRequest("auto_auth_path is required"),
		}, nil
	}

	b.StreamingBackend.SetTransparentConfig(tc)

	// Persist config to storage
	if b.StorageView != nil {
		entry, err := sdklogical.StorageEntryJSON("config", map[string]any{
			"vault_address":   b.vaultAddress,
			"max_body_size":   b.MaxBodySize,
			"timeout":         b.Timeout.String(),
			"tls_skip_verify": b.tlsSkipVerify,
			"ca_data":         b.caData,
			"auto_auth_path":  tc.AutoAuthPath,
			"default_role":    tc.DefaultAuthRole,
		})
		if err != nil {
			return &logical.Response{
				StatusCode: http.StatusInternalServerError,
				Err:        err,
			}, nil
		}
		if err := b.StorageView.Put(ctx, entry); err != nil {
			return &logical.Response{
				StatusCode: http.StatusInternalServerError,
				Err:        err,
			}, nil
		}
	}

	return &logical.Response{
		StatusCode: http.StatusOK,
		Data: map[string]any{
			"message": "configuration updated",
		},
	}, nil
}
