package main

import (
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	_ "image/png"
	"math/rand/v2"
	"mime/multipart"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/go-chi/chi/v5"
	xdraw "golang.org/x/image/draw"
)

type Entry struct {
	PID       int64  `json:"pid"`
	UID       int64  `json:"uid"`
	CreatedAt int64  `json:"created_at"`
	Content   string `json:"content"`
	Filepath  string `json:"filepath"`
	Username  string `json:"username"`
}

func handleFeed(w http.ResponseWriter, r *http.Request) {
	page, _ := strconv.Atoi(chi.URLParam(r, "page"))
	if page < 0 {
		page = 0
	}
	if page > 100 {
		page = 100
	}

	const count = 20
	offset := page * count

	rows, err := db.Query(`
		SELECT e.pid, e.uid, e.created_at, e.content, e.filepath, u.username
		FROM entry e JOIN user u ON u.uid = e.uid
		ORDER BY e.created_at DESC
		LIMIT ? OFFSET ?`, count, offset)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "Datenbankfehler")
		return
	}
	defer rows.Close()

	entries := []Entry{}
	for rows.Next() {
		var e Entry
		if err := rows.Scan(&e.PID, &e.UID, &e.CreatedAt, &e.Content, &e.Filepath, &e.Username); err != nil {
			continue
		}
		entries = append(entries, e)
	}
	writeJSON(w, http.StatusOK, entries)
}

func handleCreateEntry(w http.ResponseWriter, r *http.Request) {
	uid := uidFromContext(r.Context())

	if err := r.ParseMultipartForm(16 << 20); err != nil && err != http.ErrNotMultipart {
		writeError(w, http.StatusBadRequest, "Ungültige Anfrage")
		return
	}

	content := strings.TrimSpace(r.FormValue("content"))
	if len(content) > 1000 {
		content = content[:1000]
	}

	filepath := "none"
	if r.MultipartForm != nil {
		if files := r.MultipartForm.File["file"]; len(files) > 0 {
			if stored, err := storeImage(files[0]); err == nil && stored != "" {
				filepath = stored
			}
		}
	}

	if filepath == "none" && content == "" {
		writeError(w, http.StatusBadRequest, "Leerer Beitrag!")
		return
	}

	pid := int64(rand.IntN(999999999999))
	now := time.Now().Unix()
	if _, err := db.Exec(
		`INSERT INTO entry (pid, uid, created_at, content, filepath) VALUES (?, ?, ?, ?, ?)`,
		pid, uid, now, content, filepath,
	); err != nil {
		writeError(w, http.StatusInternalServerError, "Beitrag konnte nicht gespeichert werden")
		return
	}

	writeJSON(w, http.StatusCreated, map[string]any{
		"pid":      pid,
		"content":  content,
		"filepath": filepath,
	})
}

func handleInteractions(w http.ResponseWriter, r *http.Request) {
	writeError(w, http.StatusNotImplemented, "Voting ist noch nicht implementiert")
}

// storeImage skaliert ein hochgeladenes Bild auf max. 1024px und speichert es unter static/media.
// Hinweis: animierte GIFs werden derzeit auf das erste Frame reduziert.
func storeImage(fh *multipart.FileHeader) (string, error) {
	const maxSize = 1024

	src, err := fh.Open()
	if err != nil {
		return "", err
	}
	defer src.Close()

	img, format, err := image.Decode(src)
	if err != nil {
		return "", err
	}

	b := img.Bounds()
	width, height := b.Dx(), b.Dy()

	nw, nh := width, height
	if width > maxSize || height > maxSize {
		if width > height {
			nw, nh = maxSize, height*maxSize/width
		} else {
			nw, nh = width*maxSize/height, maxSize
		}
	}

	ext := "jpg"
	if format == "gif" {
		ext = "gif"
	}

	if err := os.MkdirAll("static/media", 0o755); err != nil {
		return "", err
	}
	name := fmt.Sprintf("static/media/%d-%d.%s", time.Now().Unix(), rand.IntN(999999), ext)

	dst := image.NewRGBA(image.Rect(0, 0, nw, nh))
	xdraw.CatmullRom.Scale(dst, dst.Bounds(), img, b, xdraw.Over, nil)

	out, err := os.Create(name)
	if err != nil {
		return "", err
	}
	defer out.Close()

	if ext == "gif" {
		err = gif.Encode(out, dst, nil)
	} else {
		err = jpeg.Encode(out, dst, &jpeg.Options{Quality: 85})
	}
	if err != nil {
		os.Remove(name)
		return "", err
	}
	return name, nil
}
