package utils

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"time"
)

const (
	debugDir        = "failed_requests"
	maxSaveBody     = 5 * 1024 * 1024 // 5 MiB, configurable
	filePerm        = 0640
	dirPerm         = 0750
	timestampFormat = "20060102T150405Z" // safe for filenames
)

// helper: generate a small random hex id
func randomHex(n int) string {
	b := make([]byte, n)
	_, _ = rand.Read(b)
	return hex.EncodeToString(b)
}

// save a failing request atomically with metadata
func saveFailedRequest(prefix, url string, reqHeaders http.Header, respHeaders http.Header, status int, body []byte, errMsg string) (string, error) {
	if err := os.MkdirAll(debugDir, dirPerm); err != nil {
		return "", fmt.Errorf("mkdir debug dir: %w", err)
	}

	ts := time.Now().UTC().Format(timestampFormat)
	id := randomHex(6)
	base := fmt.Sprintf("%s_%s_%s", prefix, ts, id)

	// Truncate body if too large
	truncated := false
	if len(body) > maxSaveBody {
		body = body[:maxSaveBody]
		truncated = true
	}

	meta := map[string]interface{}{
		"timestamp_utc": ts,
		"url":           url,
		"status":        status,
		"error":         errMsg,
		"truncated":     truncated,
		"req_headers":   reqHeaders,
		"resp_headers":  respHeaders,
	}

	metaBytes, _ := json.MarshalIndent(meta, "", "  ")

	metaTmp := filepath.Join(debugDir, base+".meta.json.tmp")
	bodyTmp := filepath.Join(debugDir, base+".body.tmp")
	metaPath := filepath.Join(debugDir, base+".meta.json")
	bodyPath := filepath.Join(debugDir, base+".body")

	// write temp + rename (atomic on POSIX filesystems)
	if err := os.WriteFile(metaTmp, metaBytes, filePerm); err != nil {
		return "", fmt.Errorf("write meta tmp: %w", err)
	}
	if err := os.Rename(metaTmp, metaPath); err != nil {
		return "", fmt.Errorf("rename meta: %w", err)
	}
	if err := os.WriteFile(bodyTmp, body, filePerm); err != nil {
		return "", fmt.Errorf("write body tmp: %w", err)
	}
	if err := os.Rename(bodyTmp, bodyPath); err != nil {
		return "", fmt.Errorf("rename body: %w", err)
	}

	return base, nil
}
