package auth

import (
	"encoding/json"
	"net/http"
	"time"

	"git.dustin.coffee/hobokenchicken/dumpsterChat/internal/middleware"
	"github.com/go-chi/chi/v5"
)

type availabilitySlot struct {
	DayOfWeek string `json:"day_of_week"`
	StartTime string `json:"start_time"` // HH:MM
	EndTime   string `json:"end_time"`   // HH:MM
}

func (h *Handler) GetMyAvailability(w http.ResponseWriter, r *http.Request) {
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}
	serverID := r.URL.Query().Get("server_id")
	if serverID == "" {
		http.Error(w, "server_id query param required", http.StatusBadRequest)
		return
	}

	rows, err := h.db.Query(
		`SELECT day_of_week, start_time, end_time FROM availability
		 WHERE user_id = $1 AND server_id = $2
		 ORDER BY array_position(ARRAY['monday','tuesday','wednesday','thursday','friday','saturday','sunday'], day_of_week), start_time`,
		userID, serverID,
	)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	var slots []availabilitySlot
	for rows.Next() {
		var s availabilitySlot
		var st, et time.Time
		if err := rows.Scan(&s.DayOfWeek, &st, &et); err != nil {
			continue
		}
		s.StartTime = st.Format("15:04")
		s.EndTime = et.Format("15:04")
		slots = append(slots, s)
	}
	if slots == nil {
		slots = []availabilitySlot{}
	}
	json.NewEncoder(w).Encode(slots)
}

func (h *Handler) PutMyAvailability(w http.ResponseWriter, r *http.Request) {
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}
	serverID := r.URL.Query().Get("server_id")
	if serverID == "" {
		http.Error(w, "server_id query param required", http.StatusBadRequest)
		return
	}

	var slots []availabilitySlot
	if err := json.NewDecoder(r.Body).Decode(&slots); err != nil {
		http.Error(w, "invalid json", http.StatusBadRequest)
		return
	}

	tx, err := h.db.Begin()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer tx.Rollback()

	if _, err := tx.Exec(`DELETE FROM availability WHERE user_id = $1 AND server_id = $2`, userID, serverID); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	for _, s := range slots {
		_, err := tx.Exec(
			`INSERT INTO availability (user_id, server_id, day_of_week, start_time, end_time) VALUES ($1,$2,$3,$4::time,$5::time)`,
			userID, serverID, s.DayOfWeek, s.StartTime, s.EndTime,
		)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	if err := tx.Commit(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(slots)
}

// GetServerAvailability returns all members' availability for a server, aggregated by user.
func (h *Handler) GetServerAvailability(w http.ResponseWriter, r *http.Request) {
	serverID := chi.URLParam(r, "serverID")

	rows, err := h.db.Query(
		`SELECT a.user_id, u.display_name, a.day_of_week, a.start_time, a.end_time
		 FROM availability a
		 JOIN users u ON u.id = a.user_id
		 WHERE a.server_id = $1
		 ORDER BY u.display_name, array_position(ARRAY['monday','tuesday','wednesday','thursday','friday','saturday','sunday'], a.day_of_week), a.start_time`,
		serverID,
	)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	type slot struct {
		DayOfWeek string `json:"day_of_week"`
		StartTime string `json:"start_time"`
		EndTime   string `json:"end_time"`
	}
	type userAvail struct {
		UserID   string `json:"user_id"`
		Username string `json:"username"`
		Slots    []slot `json:"slots"`
	}

	userMap := make(map[string]*userAvail)
	for rows.Next() {
		var uid, uname, dow string
		var st, et time.Time
		if err := rows.Scan(&uid, &uname, &dow, &st, &et); err != nil {
			continue
		}
		ua, ok := userMap[uid]
		if !ok {
			ua = &userAvail{UserID: uid, Username: uname}
			userMap[uid] = ua
		}
		ua.Slots = append(ua.Slots, slot{DayOfWeek: dow, StartTime: st.Format("15:04"), EndTime: et.Format("15:04")})
	}

	result := make([]userAvail, 0, len(userMap))
	for _, ua := range userMap {
		result = append(result, *ua)
	}
	if result == nil {
		result = []userAvail{}
	}
	json.NewEncoder(w).Encode(result)
}