package api

import (
	"database/sql"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/gin-gonic/gin"

	"github.com/rycroftapparel/workpulse-api/internal/httpapi"
)

const maxAvatarUploadBytes = 2 << 20 // 2 MB

var allowedAvatarMimes = map[string]string{
	"image/jpeg": ".jpg",
	"image/png":  ".png",
	"image/webp": ".webp",
	"image/gif":  ".gif",
}

func (s *Server) avatarDir() string {
	return filepath.Join(s.Cfg.UploadDir, "avatars")
}

func (s *Server) avatarPublicPath(filename string) string {
	return "/api/v1/uploads/avatars/" + filename
}

func sniffImageExt(head []byte, declared string) (string, bool) {
	declared = strings.ToLower(strings.TrimSpace(declared))
	if ext, ok := allowedAvatarMimes[declared]; ok {
		switch ext {
		case ".jpg":
			if len(head) >= 3 && head[0] == 0xff && head[1] == 0xd8 && head[2] == 0xff {
				return ext, true
			}
		case ".png":
			if len(head) >= 8 && string(head[:8]) == "\x89PNG\r\n\x1a\n" {
				return ext, true
			}
		case ".gif":
			if len(head) >= 6 && (string(head[:6]) == "GIF87a" || string(head[:6]) == "GIF89a") {
				return ext, true
			}
		case ".webp":
			if len(head) >= 12 && string(head[0:4]) == "RIFF" && string(head[8:12]) == "WEBP" {
				return ext, true
			}
		}
	}
	if len(head) >= 3 && head[0] == 0xff && head[1] == 0xd8 && head[2] == 0xff {
		return ".jpg", true
	}
	if len(head) >= 8 && string(head[:8]) == "\x89PNG\r\n\x1a\n" {
		return ".png", true
	}
	if len(head) >= 12 && string(head[0:4]) == "RIFF" && string(head[8:12]) == "WEBP" {
		return ".webp", true
	}
	if len(head) >= 6 && (string(head[:6]) == "GIF87a" || string(head[:6]) == "GIF89a") {
		return ".gif", true
	}
	return "", false
}

// postMeAvatar accepts multipart field "avatar" and stores the file under UploadDir/avatars.
func (s *Server) postMeAvatar(c *gin.Context) {
	uid := userID(c)
	file, err := c.FormFile("avatar")
	if err != nil {
		c.JSON(http.StatusBadRequest, httpapi.Fail("validation", "Pilih file foto (JPG, PNG, atau WebP)."))
		return
	}
	if file.Size <= 0 || file.Size > maxAvatarUploadBytes {
		c.JSON(http.StatusBadRequest, httpapi.Fail("validation", "Ukuran foto maksimal 2 MB."))
		return
	}

	src, err := file.Open()
	if err != nil {
		c.JSON(http.StatusBadRequest, httpapi.Fail("validation", "File tidak dapat dibaca."))
		return
	}
	defer src.Close()

	head := make([]byte, 512)
	n, _ := io.ReadFull(src, head)
	head = head[:n]
	ext, ok := sniffImageExt(head, file.Header.Get("Content-Type"))
	if !ok {
		c.JSON(http.StatusBadRequest, httpapi.Fail("validation", "Format foto harus JPG, PNG, WebP, atau GIF."))
		return
	}

	dir := s.avatarDir()
	if err := os.MkdirAll(dir, 0o755); err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("storage", "Gagal menyiapkan penyimpanan foto."))
		return
	}

	filename := fmt.Sprintf("user_%d%s", uid, ext)
	destPath := filepath.Join(dir, filename)
	dst, err := os.Create(destPath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("storage", "Gagal menyimpan foto."))
		return
	}
	if _, err := dst.Write(head); err != nil {
		_ = dst.Close()
		c.JSON(http.StatusInternalServerError, httpapi.Fail("storage", "Gagal menyimpan foto."))
		return
	}
	if _, err := io.Copy(dst, src); err != nil {
		_ = dst.Close()
		c.JSON(http.StatusInternalServerError, httpapi.Fail("storage", "Gagal menyimpan foto."))
		return
	}
	_ = dst.Close()

	publicURL := s.avatarPublicPath(filename)
	ctx, cancel := s.ctx(c)
	defer cancel()
	if _, err := s.DB.ExecContext(ctx, `UPDATE users SET avatar_url = $1 WHERE id = $2`, publicURL, uid); err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("db", "Gagal memperbarui profil."))
		return
	}
	s.recordUserActivity(c, ctx, uid, "profile.avatar_upload", "Profile photo updated", "New avatar uploaded", nil)
	s.getMe(c)
}

// deleteMeAvatar removes the stored avatar file and clears avatar_url.
func (s *Server) deleteMeAvatar(c *gin.Context) {
	uid := userID(c)
	ctx, cancel := s.ctx(c)
	defer cancel()
	var avatar sql.NullString
	err := s.DB.QueryRowContext(ctx, `SELECT avatar_url FROM users WHERE id = $1`, uid).Scan(&avatar)
	if err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("db", "Gagal memperbarui profil."))
		return
	}
	if avatar.Valid {
		s.removeAvatarFiles(uid, avatar.String)
	}
	if _, err := s.DB.ExecContext(ctx, `UPDATE users SET avatar_url = NULL WHERE id = $1`, uid); err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("db", "Gagal memperbarui profil."))
		return
	}
	s.recordUserActivity(c, ctx, uid, "profile.avatar_remove", "Profile photo removed", "Avatar was cleared", nil)
	s.getMe(c)
}

func (s *Server) removeAvatarFiles(uid uint64, avatarURL string) {
	avatarURL = strings.TrimSpace(avatarURL)
	if avatarURL == "" || strings.HasPrefix(strings.ToLower(avatarURL), "http") {
		return
	}
	const prefix = "/api/v1/uploads/avatars/"
	if idx := strings.LastIndex(avatarURL, prefix); idx >= 0 {
		name := avatarURL[idx+len(prefix):]
		_ = os.Remove(filepath.Join(s.avatarDir(), filepath.Base(name)))
	}
	for _, ext := range []string{".jpg", ".jpeg", ".png", ".webp", ".gif"} {
		_ = os.Remove(filepath.Join(s.avatarDir(), fmt.Sprintf("user_%d%s", uid, ext)))
	}
}
