// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package audit

import (
	"context"
	"errors"
	"fmt"
	"io"
	"strings"

	"github.com/hashicorp/go-hclog"
	"github.com/hashicorp/vault/sdk/helper/salt"
	"github.com/hashicorp/vault/sdk/logical"
)

var (
	_ Formatter = (*EntryFormatterWriter)(nil)
	_ Writer    = (*EntryFormatterWriter)(nil)
)

// Salt returns a new salt with default configuration and no storage usage, and no error.
func (s *nonPersistentSalt) Salt(_ context.Context) (*salt.Salt, error) {
	return salt.NewNonpersistentSalt(), nil
}

// NewEntryFormatterWriter should be used to create a new EntryFormatterWriter.
// Deprecated: Please move to using eventlogger.Event via EntryFormatter and a sink.
func NewEntryFormatterWriter(config FormatterConfig, formatter Formatter, writer Writer) (*EntryFormatterWriter, error) {
	switch {
	case formatter == nil:
		return nil, errors.New("cannot create a new audit formatter writer with nil formatter")
	case writer == nil:
		return nil, errors.New("cannot create a new audit formatter writer with nil formatter")
	}

	fw := &EntryFormatterWriter{
		Formatter: formatter,
		Writer:    writer,
		config:    config,
	}

	return fw, nil
}

// FormatAndWriteRequest attempts to format the specified logical.LogInput into an RequestEntry,
// and then write the request using the specified io.Writer.
// Deprecated: Please move to using eventlogger.Event via EntryFormatter and a sink.
func (f *EntryFormatterWriter) FormatAndWriteRequest(ctx context.Context, w io.Writer, in *logical.LogInput) error {
	switch {
	case in == nil || in.Request == nil:
		return fmt.Errorf("request to request-audit a nil request")
	case w == nil:
		return fmt.Errorf("writer for audit request is nil")
	case f.Formatter == nil:
		return fmt.Errorf("no formatter specifed")
	case f.Writer == nil:
		return fmt.Errorf("no writer specified")
	}

	reqEntry, err := f.Formatter.FormatRequest(ctx, in)
	if err != nil {
		return err
	}

	return f.Writer.WriteRequest(w, reqEntry)
}

// FormatAndWriteResponse attempts to format the specified logical.LogInput into an ResponseEntry,
// and then write the response using the specified io.Writer.
// Deprecated: Please move to using eventlogger.Event via EntryFormatter and a sink.
func (f *EntryFormatterWriter) FormatAndWriteResponse(ctx context.Context, w io.Writer, in *logical.LogInput) error {
	switch {
	case in == nil || in.Request == nil:
		return errors.New("request to response-audit a nil request")
	case w == nil:
		return errors.New("writer for audit request is nil")
	case f.Formatter == nil:
		return errors.New("no formatter specified")
	case f.Writer == nil:
		return errors.New("no writer specified")
	}

	respEntry, err := f.FormatResponse(ctx, in)
	if err != nil {
		return err
	}

	return f.Writer.WriteResponse(w, respEntry)
}

// NewTemporaryFormatter creates a formatter not backed by a persistent salt
func NewTemporaryFormatter(requiredFormat, prefix string) (*EntryFormatterWriter, error) {
	cfg, err := NewFormatterConfig(WithFormat(requiredFormat))
	if err != nil {
		return nil, err
	}

	eventFormatter, err := NewEntryFormatter("sys/audit/test", cfg, &nonPersistentSalt{}, hclog.NewNullLogger(), WithPrefix(prefix))
	if err != nil {
		return nil, err
	}

	var w Writer

	switch {
	case strings.EqualFold(requiredFormat, JSONxFormat.String()):
		w = &JSONxWriter{Prefix: prefix}
	default:
		w = &JSONWriter{Prefix: prefix}
	}

	fw, err := NewEntryFormatterWriter(cfg, eventFormatter, w)
	if err != nil {
		return nil, err
	}

	return fw, nil
}
