package voice

import (
	"database/sql"
	"encoding/json"
	"net/http"

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

type Handler struct {
	db      *sql.DB
	client  *Client
	hub     *gateway.Hub
	checker *permissions.Checker
}

func NewHandler(db *sql.DB, client *Client, hub *gateway.Hub) *Handler {
	return &Handler{
		db:      db,
		client:  client,
		hub:     hub,
		checker: permissions.NewChecker(db),
	}
}

func (h *Handler) RegisterRoutes(r chi.Router) {
	r.Use(middleware.RequireAuth)
	r.Post("/token", h.GetToken)
	r.Get("/rooms/{roomID}/participants", h.GetParticipants)
	r.Post("/rooms/{roomID}/participants/{userID}/mute", h.MuteParticipant)
}

type tokenRequest struct {
	RoomName string `json:"room_name"`
}

type tokenResponse struct {
	Token      string `json:"token"`
	LiveKitURL string `json:"livekit_url"`
}

// @Summary Get voice token
// @Description Get a LiveKit access token for a voice room
// @Tags voice
// @Accept json
// @Produce json
// @Security SessionAuth
// @Param body body tokenRequest true "Room data"
// @Success 200 {object} tokenResponse
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /voice/token [post]
func (h *Handler) GetToken(w http.ResponseWriter, r *http.Request) {
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
		return
	}

	var req tokenRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}

	if req.RoomName == "" {
		http.Error(w, `{"error":"missing room_name"}`, http.StatusBadRequest)
		return
	}

	// Get username for the identity
	var username string
	err := h.db.QueryRowContext(r.Context(), `SELECT username FROM users WHERE id = $1`, userID).Scan(&username)
	if err != nil {
		http.Error(w, `{"error":"user not found"}`, http.StatusNotFound)
		return
	}

	// Create room if it doesn't exist
	_, err = h.client.CreateRoom(req.RoomName)
	if err != nil {
		// Room might already exist, that's fine
	}

	token, err := h.client.GenerateToken(req.RoomName, userID, username)
	if err != nil {
		http.Error(w, `{"error":"token generation failed"}`, http.StatusInternalServerError)
		return
	}

	// Broadcast voice join event
	h.hub.BroadcastEvent(gateway.Event{
		Type: "VOICE_JOIN",
		Data: map[string]interface{}{
			"room_name": req.RoomName,
			"user_id":   userID,
			"username":  username,
		},
	})

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(tokenResponse{
		Token:      token,
		LiveKitURL: h.client.url,
	})
}

// @Summary Get voice room participants
// @Description Get participants in a voice room
// @Tags voice
// @Produce json
// @Param roomID path string true "Room ID"
// @Success 200 {array} object
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /voice/rooms/{roomID}/participants [get]
func (h *Handler) GetParticipants(w http.ResponseWriter, r *http.Request) {
	roomID := chi.URLParam(r, "roomID")
	if roomID == "" {
		http.Error(w, `{"error":"missing room ID"}`, http.StatusBadRequest)
		return
	}

	participants, err := h.client.ListParticipants(roomID)
	if err != nil {
		http.Error(w, `{"error":"failed to list participants"}`, http.StatusInternalServerError)
		return
	}

	type participantInfo struct {
		Identity string `json:"identity"`
		State    string `json:"state"`
	}

	result := make([]participantInfo, 0, len(participants))
	for _, p := range participants {
		state := "connected"
		if p.State == livekit.ParticipantInfo_DISCONNECTED {
			state = "disconnected"
		}
		result = append(result, participantInfo{
			Identity: p.Identity,
			State:    state,
		})
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(result)
}

// @Summary Mute a participant
// @Description Mute a participant's tracks in a voice room (requires MUTE_MEMBERS permission)
// @Tags voice
// @Security SessionAuth
// @Param roomID path string true "Room ID"
// @Param userID path string true "User ID (Identity)"
// @Success 200 {object} map[string]string
// @Router /voice/rooms/{roomID}/participants/{userID}/mute [post]
func (h *Handler) MuteParticipant(w http.ResponseWriter, r *http.Request) {
	roomID := chi.URLParam(r, "roomID")
	targetIdentity := chi.URLParam(r, "userID")
	
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
		return
	}

	// Find the server_id for this channel to check permissions.
	var serverID string
	err := h.db.QueryRowContext(r.Context(), `SELECT server_id FROM channels WHERE id = $1`, roomID).Scan(&serverID)
	if err != nil {
		if err == sql.ErrNoRows {
			http.Error(w, `{"error":"room not found or not a server channel"}`, http.StatusNotFound)
		} else {
			http.Error(w, `{"error":"database error"}`, http.StatusInternalServerError)
		}
		return
	}

	hasPerm, err := h.checker.CheckChannelPermission(r.Context(), serverID, userID, roomID, permissions.MUTE_MEMBERS)
	if err != nil {
		http.Error(w, `{"error":"failed to check permissions"}`, http.StatusInternalServerError)
		return
	}
	if !hasPerm {
		http.Error(w, `{"error":"forbidden: missing MUTE_MEMBERS permission"}`, http.StatusForbidden)
		return
	}

	if err := h.client.MuteParticipant(roomID, targetIdentity); err != nil {
		http.Error(w, `{"error":"failed to mute participant"}`, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]string{"status": "success"})
}
