// Package fileutil enthält gemeinsame Datei-Hilfsfunktionen für Upload-Handler (V1, N6).
package fileutil

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// SaveUploadedFile speichert einen Datei-Stream in uploadDir/{tenantSlug}/ und
// gibt den relativen HTTP-Pfad (/uploads/{tenantSlug}/filename) sowie die
// Anzahl geschriebener Bytes zurück.
//
// V1: Gemeinsame Upload-Logik — ersetzt 3× duplizierte Implementierung.
// N6: Tenant-spezifisches Verzeichnis statt gemeinsamer Ablage.
func SaveUploadedFile(file io.Reader, originalFilename, title, uploadDir, tenantSlug string) (storagePath string, size int64, err error) {
	safeSlug := sanitize(tenantSlug)
	if safeSlug == "" {
		safeSlug = "default"
	}
	tenantDir := filepath.Join(uploadDir, safeSlug)
	if mkErr := os.MkdirAll(tenantDir, 0755); mkErr != nil {
		return "", 0, fmt.Errorf("fileutil: mkdir %s: %w", tenantDir, mkErr)
	}

	ext := filepath.Ext(originalFilename)
	safeTitle := sanitize(title)
	if safeTitle == "" {
		safeTitle = "file"
	}
	filename := fmt.Sprintf("%d_%s%s", time.Now().UnixNano(), safeTitle, ext)
	destPath := filepath.Join(tenantDir, filename)

	dest, createErr := os.Create(destPath)
	if createErr != nil {
		return "", 0, fmt.Errorf("fileutil: create %s: %w", destPath, createErr)
	}
	defer dest.Close()

	n, copyErr := io.Copy(dest, file)
	if copyErr != nil {
		os.Remove(destPath) //nolint:errcheck
		return "", 0, fmt.Errorf("fileutil: write %s: %w", destPath, copyErr)
	}

	return "/uploads/" + safeSlug + "/" + filename, n, nil
}

// sanitize konvertiert einen String in einen sicheren Dateinamen-Bestandteil
// (nur a-z, A-Z, 0-9, -, _; maximal 40 Zeichen).
func sanitize(s string) string {
	var b strings.Builder
	for _, r := range s {
		if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '-' || r == '_' {
			b.WriteRune(r)
		} else {
			b.WriteRune('_')
		}
	}
	out := b.String()
	if len(out) > 40 {
		out = out[:40]
	}
	return out
}
