package channel

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

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

type Handler struct {
	db      *sql.DB
	checker *permissions.Checker
}

func NewHandler(db *sql.DB, checker *permissions.Checker) *Handler {
	return &Handler{db: db, checker: checker}
}

func (h *Handler) RegisterRoutes(r chi.Router) {
	r.Post("/", h.Create)
	r.Get("/", h.List)
	r.Get("/{channelID}", h.Get)
	r.Patch("/{channelID}", h.Update)
	r.Delete("/{channelID}", h.Delete)
	r.Post("/{channelID}/threads", h.CreateThread)
	r.Get("/{channelID}/threads", h.ListThreads)
	r.Patch("/threads/{threadID}", h.UpdateThread)
	h.registerForumRoutes(r)
	h.registerCalendarRoutes(r)
	h.registerDocRoutes(r)
	h.registerListRoutes(r)
	h.registerOverrideRoutes(r)
}

type createChannelRequest struct {
	Name     string  `json:"name"`
	Type     string  `json:"type"`
	Category string  `json:"category"`
	Position *int    `json:"position"`
	GroupID  *string `json:"group_id"`
}

type channelResponse struct {
	ID              string  `json:"id"`
	ServerID        string  `json:"server_id"`
	Name            string  `json:"name"`
	Type            string  `json:"type"`
	Category        string  `json:"category"`
	Position        int     `json:"position"`
	SlowmodeSeconds int     `json:"slowmode_seconds"`
	GroupID         *string `json:"group_id"`
	CreatedAt       string  `json:"created_at"`
}

// isMember checks whether the given user is a member of the given server.
func (h *Handler) isMember(ctx context.Context, userID, serverID string) (bool, error) {
	var exists bool
	err := h.db.QueryRowContext(ctx, `
		SELECT EXISTS(SELECT 1 FROM members WHERE user_id = $1 AND server_id = $2)
	`, userID, serverID).Scan(&exists)
	return exists, err
}

// serverIDForChannel returns the server_id that owns the given channel.
func (h *Handler) serverIDForChannel(ctx context.Context, channelID string) (string, error) {
	var serverID string
	err := h.db.QueryRowContext(ctx, `
		SELECT server_id FROM channels WHERE id = $1
	`, channelID).Scan(&serverID)
	return serverID, err
}

// @Summary Create a channel
// @Description Create a new channel in a server
// @Tags channels
// @Accept json
// @Produce json
// @Security SessionAuth
// @Param body body createChannelRequest true "Channel data"
// @Success 201 {object} channelResponse
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Failure 403 {object} map[string]string
// @Router /servers/{serverID}/channels [post]
func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
		return
	}

	serverID := chi.URLParam(r, "serverID")
	if serverID == "" {
		http.Error(w, `{"error":"serverID is required"}`, http.StatusBadRequest)
		return
	}

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

	member, err := h.isMember(r.Context(), userID, serverID)
	if err != nil {
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}
	if !member {
		http.Error(w, `{"error":"not a member of this server"}`, http.StatusForbidden)
		return
	}

	channelType := req.Type
	if channelType == "" {
		channelType = "text"
	}
	category := req.Category
	if category == "" {
		category = "general"
	}
	position := 0
	if req.Position != nil {
		position = *req.Position
	}

	var ch channelResponse
	err = h.db.QueryRowContext(r.Context(), `
		INSERT INTO channels (server_id, name, type, category, position, slowmode_seconds, group_id)
		VALUES ($1, $2, $3, $4, $5, 0, $6)
		RETURNING id, server_id, name, type, category, position, slowmode_seconds, group_id, created_at
	`, serverID, req.Name, channelType, category, position, req.GroupID).Scan(
		&ch.ID, &ch.ServerID, &ch.Name, &ch.Type, &ch.Category, &ch.Position, &ch.SlowmodeSeconds, &ch.GroupID, &ch.CreatedAt,
	)
	if err != nil {
		http.Error(w, `{"error":"failed to create channel (name may already exist in this server)"}`, http.StatusConflict)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(ch)
}

// @Summary List channels
// @Description List channels in a server
// @Tags channels
// @Produce json
// @Security SessionAuth
// @Param server_id query string true "Server ID"
// @Success 200 {array} channelResponse
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Failure 403 {object} map[string]string
// @Router /servers/{serverID}/channels [get]
func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
		return
	}

	serverID := chi.URLParam(r, "serverID")
	if serverID == "" {
		http.Error(w, `{"error":"server_id query parameter is required"}`, http.StatusBadRequest)
		return
	}

	member, err := h.isMember(r.Context(), userID, serverID)
	if err != nil {
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}
	if !member {
		http.Error(w, `{"error":"not a member of this server"}`, http.StatusForbidden)
		return
	}

	rows, err := h.db.QueryContext(r.Context(), `
		SELECT id, server_id, name, type, category, position, slowmode_seconds, group_id, created_at
		FROM channels
		WHERE server_id = $1
		ORDER BY position, name
	`, serverID)
	if err != nil {
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	channels := make([]channelResponse, 0)
	for rows.Next() {
		var ch channelResponse
		if err := rows.Scan(&ch.ID, &ch.ServerID, &ch.Name, &ch.Type, &ch.Category, &ch.Position, &ch.SlowmodeSeconds, &ch.GroupID, &ch.CreatedAt); err != nil {
			http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
			return
		}
		
		// Check VIEW_CHANNEL permission
		allowed, err := h.checker.CheckChannelPermission(r.Context(), serverID, userID, ch.ID, permissions.VIEW_CHANNEL)
		if err != nil {
			http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
			return
		}
		if allowed {
			channels = append(channels, ch)
		}
	}
	if err := rows.Err(); err != nil {
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}

	// Filter out channels the user doesn't have VIEW_CHANNEL permission for.
	filtered := make([]channelResponse, 0, len(channels))
	for _, ch := range channels {
		allowed, checkErr := h.checker.CheckChannelPermission(r.Context(), serverID, userID, ch.ID, permissions.VIEW_CHANNEL)
		if checkErr != nil {
			continue
		}
		if allowed {
			filtered = append(filtered, ch)
		}
	}

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

// @Summary Get a channel
// @Description Get a channel by ID
// @Tags channels
// @Produce json
// @Security SessionAuth
// @Param channelID path string true "Channel ID"
// @Success 200 {object} channelResponse
// @Failure 401 {object} map[string]string
// @Failure 403 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /servers/{serverID}/channels/{channelID} [get]
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
		return
	}

	channelID := chi.URLParam(r, "channelID")

	var ch channelResponse
	err := h.db.QueryRowContext(r.Context(), `
		SELECT id, server_id, name, type, category, position, slowmode_seconds, group_id, created_at
		FROM channels WHERE id = $1
	`, channelID).Scan(&ch.ID, &ch.ServerID, &ch.Name, &ch.Type, &ch.Category, &ch.Position, &ch.SlowmodeSeconds, &ch.GroupID, &ch.CreatedAt)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			http.Error(w, `{"error":"channel not found"}`, http.StatusNotFound)
			return
		}
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}

	member, err := h.isMember(r.Context(), userID, ch.ServerID)
	if err != nil {
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}
	if !member {
		http.Error(w, `{"error":"not a member of this server"}`, http.StatusForbidden)
		return
	}

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

type updateChannelRequest struct {
	Name            *string `json:"name"`
	Type            *string `json:"type"`
	Category        *string `json:"category"`
	Position        *int    `json:"position"`
	SlowmodeSeconds *int    `json:"slowmode_seconds"`
	GroupID         *string `json:"group_id"`
}

// @Summary Update a channel
// @Description Update a channel's properties
// @Tags channels
// @Accept json
// @Produce json
// @Security SessionAuth
// @Param channelID path string true "Channel ID"
// @Param body body updateChannelRequest true "Channel update data"
// @Success 200 {object} channelResponse
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Failure 403 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /servers/{serverID}/channels/{channelID} [patch]
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
		return
	}

	channelID := chi.URLParam(r, "channelID")

	var req updateChannelRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
		return
	}
	if req.Name == nil && req.Type == nil && req.Category == nil && req.Position == nil && req.SlowmodeSeconds == nil && req.GroupID == nil {
		http.Error(w, `{"error":"nothing to update"}`, http.StatusBadRequest)
		return
	}

	// Look up server ownership and verify membership
	serverID, err := h.serverIDForChannel(r.Context(), channelID)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			http.Error(w, `{"error":"channel not found"}`, http.StatusNotFound)
			return
		}
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}

	// Verify membership and MANAGE_CHANNELS permission
	member, err := h.isMember(r.Context(), userID, serverID)
	if err != nil {
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}
	if !member {
		http.Error(w, `{"error":"not a member of this server"}`, http.StatusForbidden)
		return
	}

	allowed, err := h.checker.CheckPermission(r.Context(), serverID, userID, permissions.MANAGE_CHANNELS)
	if err != nil {
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}
	if !allowed {
		http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
		return
	}

	var ch channelResponse
	err = h.db.QueryRowContext(r.Context(), `
		UPDATE channels
		SET name = COALESCE($1, name),
			type = COALESCE($2, type),
			category = COALESCE($3, category),
			position = COALESCE($4, position),
			slowmode_seconds = COALESCE($5, slowmode_seconds),
			group_id = COALESCE($7, group_id)
		WHERE id = $6
		RETURNING id, server_id, name, type, category, position, slowmode_seconds, group_id, created_at
	`, req.Name, req.Type, req.Category, req.Position, req.SlowmodeSeconds, channelID, req.GroupID).Scan(
		&ch.ID, &ch.ServerID, &ch.Name, &ch.Type, &ch.Category, &ch.Position, &ch.SlowmodeSeconds, &ch.GroupID, &ch.CreatedAt,
	)
	if err != nil {
		http.Error(w, `{"error":"failed to update channel"}`, http.StatusInternalServerError)
		return
	}

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

// @Summary Delete a channel
// @Description Delete a channel (server owner only)
// @Tags channels
// @Security SessionAuth
// @Param channelID path string true "Channel ID"
// @Success 204
// @Failure 401 {object} map[string]string
// @Failure 403 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /servers/{serverID}/channels/{channelID} [delete]
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
	userID, ok := middleware.UserIDFromContext(r.Context())
	if !ok {
		http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
		return
	}

	channelID := chi.URLParam(r, "channelID")

	serverID, err := h.serverIDForChannel(r.Context(), channelID)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			http.Error(w, `{"error":"channel not found"}`, http.StatusNotFound)
			return
		}
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}

	// Only the server owner can delete channels
	var ownerID string
	err = h.db.QueryRowContext(r.Context(), `
		SELECT owner_id FROM servers WHERE id = $1
	`, serverID).Scan(&ownerID)
	if err != nil {
		http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError)
		return
	}
	if ownerID != userID {
		// Not the owner; check MANAGE_CHANNELS permission
		allowed, err := h.checker.CheckPermission(r.Context(), serverID, userID, permissions.MANAGE_CHANNELS)
		if err != nil || !allowed {
			http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
			return
		}
	}

	_, err = h.db.ExecContext(r.Context(), `
		DELETE FROM channels WHERE id = $1
	`, channelID)
	if err != nil {
		http.Error(w, `{"error":"failed to delete channel"}`, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
