package main

// Saved-coordinate book (wdcoordinates_edit equivalent).
// CRUD + teleport-to-coord via bridge proxy.

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

	"github.com/dbledeez/panel/controller/internal/db"
)

// coordRow mirrors the empyrion_coords table.
type coordRow struct {
	ID        int64   `json:"id"`
	Name      string  `json:"name"`
	Playfield string  `json:"playfield"`
	X         float64 `json:"x"`
	Y         float64 `json:"y"`
	Z         float64 `json:"z"`
	Notes     string  `json:"notes,omitempty"`
	CreatedBy string  `json:"createdBy,omitempty"`
	CreatedAt string  `json:"createdAt,omitempty"`
}

// ---------------------------------------------------------------------------
// GET /api/instances/{id}/empyrion/coords
// ---------------------------------------------------------------------------

func (h *httpServer) handleEmpyrionCoordsList(w http.ResponseWriter, r *http.Request) {
	instanceID := r.PathValue("id")

	rows, err := h.db.Pool().Query(r.Context(), `
		SELECT id, name, playfield, x, y, z, COALESCE(notes,''), COALESCE(created_by,''), created_at
		FROM empyrion_coords
		WHERE instance_id = $1
		ORDER BY name`, instanceID)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "db", err.Error())
		return
	}
	defer rows.Close()

	out := make([]coordRow, 0, 64)
	for rows.Next() {
		var c coordRow
		var created any
		if err := rows.Scan(&c.ID, &c.Name, &c.Playfield, &c.X, &c.Y, &c.Z, &c.Notes, &c.CreatedBy, &created); err != nil {
			continue
		}
		c.CreatedAt = formatTS(created)
		out = append(out, c)
	}
	writeJSON(w, http.StatusOK, map[string]any{"coords": out})
}

// ---------------------------------------------------------------------------
// POST /api/instances/{id}/empyrion/coords
// ---------------------------------------------------------------------------

func (h *httpServer) handleEmpyrionCoordsCreate(w http.ResponseWriter, r *http.Request) {
	instanceID := r.PathValue("id")

	var req struct {
		Name      string   `json:"name"`
		Playfield string   `json:"playfield"`
		X         float64  `json:"x"`
		Y         float64  `json:"y"`
		Z         float64  `json:"z"`
		Notes     *string  `json:"notes,omitempty"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "bad_json", err.Error())
		return
	}
	if req.Name == "" || req.Playfield == "" {
		writeError(w, http.StatusBadRequest, "missing", "name and playfield are required")
		return
	}

	// Pull created_by from session if available.
	createdBy := ""
	if u, ok := r.Context().Value(sessionUserKey{}).(*db.UserRow); ok {
		createdBy = u.Email
	}

	notes := ""
	if req.Notes != nil {
		notes = *req.Notes
	}

	var row coordRow
	var created any
	err := h.db.Pool().QueryRow(r.Context(), `
		INSERT INTO empyrion_coords (instance_id, name, playfield, x, y, z, notes, created_by)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
		ON CONFLICT (instance_id, name) DO UPDATE SET
			playfield = EXCLUDED.playfield,
			x = EXCLUDED.x,
			y = EXCLUDED.y,
			z = EXCLUDED.z,
			notes = EXCLUDED.notes,
			created_by = EXCLUDED.created_by
		RETURNING id, name, playfield, x, y, z, COALESCE(notes,''), COALESCE(created_by,''), created_at`,
		instanceID, req.Name, req.Playfield, req.X, req.Y, req.Z, notes, createdBy,
	).Scan(&row.ID, &row.Name, &row.Playfield, &row.X, &row.Y, &row.Z, &row.Notes, &row.CreatedBy, &created)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "db", err.Error())
		return
	}
	row.CreatedAt = formatTS(created)

	writeJSON(w, http.StatusCreated, row)
}

// ---------------------------------------------------------------------------
// DELETE /api/instances/{id}/empyrion/coords/{coordId}
// ---------------------------------------------------------------------------

func (h *httpServer) handleEmpyrionCoordsDelete(w http.ResponseWriter, r *http.Request) {
	instanceID := r.PathValue("id")
	coordID, err := strconv.ParseInt(r.PathValue("coordId"), 10, 64)
	if err != nil || coordID <= 0 {
		writeError(w, http.StatusBadRequest, "bad_coord_id", "coordId must be a positive integer")
		return
	}

	tag, err := h.db.Pool().Exec(r.Context(),
		`DELETE FROM empyrion_coords WHERE id=$1 AND instance_id=$2`,
		coordID, instanceID)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "db", err.Error())
		return
	}
	if tag.RowsAffected() == 0 {
		writeError(w, http.StatusNotFound, "not_found", "coordinate not found")
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

// ---------------------------------------------------------------------------
// POST /api/instances/{id}/empyrion/coords/{coordId}/teleport
// ---------------------------------------------------------------------------

func (h *httpServer) handleEmpyrionCoordsTeleport(w http.ResponseWriter, r *http.Request) {
	instanceID := r.PathValue("id")
	coordID, err := strconv.ParseInt(r.PathValue("coordId"), 10, 64)
	if err != nil || coordID <= 0 {
		writeError(w, http.StatusBadRequest, "bad_coord_id", "coordId must be a positive integer")
		return
	}

	var req struct {
		PlayerID int `json:"playerId"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "bad_json", err.Error())
		return
	}
	if req.PlayerID <= 0 {
		writeError(w, http.StatusBadRequest, "missing", "playerId is required")
		return
	}

	// Look up the coord.
	var c coordRow
	var created any
	err = h.db.Pool().QueryRow(r.Context(), `
		SELECT id, name, playfield, x, y, z, COALESCE(notes,''), COALESCE(created_by,''), created_at
		FROM empyrion_coords WHERE id=$1 AND instance_id=$2`,
		coordID, instanceID,
	).Scan(&c.ID, &c.Name, &c.Playfield, &c.X, &c.Y, &c.Z, &c.Notes, &c.CreatedBy, &created)
	if err != nil {
		writeError(w, http.StatusNotFound, "not_found", "coordinate not found")
		return
	}

	// Resolve bridge target.
	tgt, err := h.resolveEmpyrionTarget(r.Context(), instanceID)
	if err != nil {
		writeError(w, http.StatusServiceUnavailable, "no_bridge", err.Error())
		return
	}

	// POST to bridge: /api/v1/players/{playerId}/teleport with {x,y,z,playfield}.
	teleportBody := map[string]any{
		"x":         c.X,
		"y":         c.Y,
		"z":         c.Z,
		"playfield": c.Playfield,
	}
	bodyBytes, _ := json.Marshal(teleportBody)
	ok, msg := postBridgeJSON(r.Context(), tgt, "/api/v1/players/"+strconv.Itoa(req.PlayerID)+"/teleport", bodyBytes)
	if !ok {
		writeError(w, http.StatusBadGateway, "bridge_failed", msg)
		return
	}

	writeJSON(w, http.StatusOK, map[string]any{
		"teleported": true,
		"playerId":   req.PlayerID,
		"coordId":    coordID,
		"coordName":  c.Name,
	})
}
