package attachment

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"
	"strings"

	"github.com/memohai/memoh/internal/media"
)

// MapMediaType maps attachment type strings to media types.
func MapMediaType(rawType string) media.MediaType {
	switch strings.ToLower(strings.TrimSpace(rawType)) {
	case "image", "gif":
		return media.MediaTypeImage
	case "audio", "voice":
		return media.MediaTypeAudio
	case "video":
		return media.MediaTypeVideo
	default:
		return media.MediaTypeFile
	}
}

// NormalizeMime normalizes MIME to lowercase token form.
func NormalizeMime(raw string) string {
	mime := strings.ToLower(strings.TrimSpace(raw))
	if mime == "" {
		return ""
	}
	if idx := strings.Index(mime, ";"); idx >= 0 {
		mime = strings.TrimSpace(mime[:idx])
	}
	return mime
}

// MimeFromDataURL extracts MIME from a data URL.
func MimeFromDataURL(raw string) string {
	value := strings.TrimSpace(raw)
	lower := strings.ToLower(value)
	if !strings.HasPrefix(lower, "data:") {
		return ""
	}
	rest := value[len("data:"):]
	if idx := strings.Index(rest, ";"); idx >= 0 {
		return NormalizeMime(rest[:idx])
	}
	if idx := strings.Index(rest, ","); idx >= 0 {
		return NormalizeMime(rest[:idx])
	}
	return ""
}

// ResolveMime resolves source MIME and sniffed MIME into final MIME.
func ResolveMime(mediaType media.MediaType, sourceMime, sniffedMime string) string {
	source := NormalizeMime(sourceMime)
	sniffed := NormalizeMime(sniffedMime)
	sourceGeneric := source == "" || source == "application/octet-stream"

	if mediaType == media.MediaTypeImage {
		if strings.HasPrefix(source, "image/") {
			return source
		}
		if strings.HasPrefix(sniffed, "image/") {
			return sniffed
		}
		if !sourceGeneric {
			return source
		}
		if sniffed != "" {
			return sniffed
		}
		return "application/octet-stream"
	}

	if !sourceGeneric {
		return source
	}
	if sniffed != "" {
		return sniffed
	}
	if source != "" {
		return source
	}
	return "application/octet-stream"
}

// PrepareReaderAndMime reads a small prefix for MIME sniffing and replays it.
func PrepareReaderAndMime(reader io.Reader, mediaType media.MediaType, sourceMime string) (io.Reader, string, error) {
	if reader == nil {
		return nil, "", fmt.Errorf("reader is required")
	}
	header := make([]byte, 512)
	n, err := reader.Read(header)
	if err != nil && err != io.EOF {
		return nil, "", fmt.Errorf("read mime sniff bytes: %w", err)
	}
	header = header[:n]
	sniffed := ""
	if len(header) > 0 {
		sniffed = NormalizeMime(http.DetectContentType(header))
	}
	finalMime := ResolveMime(mediaType, sourceMime, sniffed)
	return io.MultiReader(bytes.NewReader(header), reader), finalMime, nil
}

// NormalizeBase64DataURL normalizes raw base64 into a data URL.
func NormalizeBase64DataURL(input, mime string) string {
	value := strings.TrimSpace(input)
	if value == "" {
		return ""
	}
	if strings.HasPrefix(strings.ToLower(value), "data:") {
		return value
	}
	mime = NormalizeMime(mime)
	if mime == "" {
		mime = "application/octet-stream"
	}
	return "data:" + mime + ";base64," + value
}

// DecodeBase64 decodes both raw base64 and data URL base64 content.
// The returned reader is bounded to maxBytes+1 for caller-side size validation.
func DecodeBase64(input string, maxBytes int64) (io.Reader, error) {
	value := strings.TrimSpace(input)
	if value == "" {
		return nil, fmt.Errorf("base64 payload is empty")
	}
	if strings.HasPrefix(strings.ToLower(value), "data:") {
		if idx := strings.Index(value, ","); idx >= 0 {
			value = value[idx+1:]
		}
	}
	decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(value))
	return io.LimitReader(decoder, maxBytes+1), nil
}
