package server

import (
	"net/http"
	"strconv"

	"github.com/labstack/echo/v4"

	"git.maze.io/go/ham/protocol/meshcore"
	"git.maze.io/ham/hamview/schema"
)

// handleGetMeshCore returns aggregated statistics for the MeshCore network.
//
// Endpoint: GET /api/v1/meshcore
//
// Response: 200 OK
//
//	MeshCoreStats - Network statistics including message counts and packet timeline
//
// Response: 500 Internal Server Error
//
//	ErrorResponse - Error retrieving statistics
//
// Example Request:
//
//	GET /api/v1/meshcore
//
// Example Response:
//
//	{
//	  "messages": 150234,
//	  "nodes": 127,
//	  "receivers": 8,
//	  "packets": {
//	    "timestamps": [1709650800, 1709654400, 1709658000],
//	    "packets": [142, 203, 178]
//	  }
//	}
func (s *Server) handleGetMeshCore(c echo.Context) error {
	stats, err := schema.GetMeshCoreStats(c.Request().Context())
	if err != nil {
		return s.apiError(c, err)
	}
	return c.JSON(http.StatusOK, stats)
}

// handleGetMeshCoreGroups returns a list of public MeshCore groups/channels.
//
// Endpoint: GET /api/v1/meshcore/groups
//
// Response: 200 OK
//
//	[]MeshCoreGroup - Array of public group objects
//
// Response: 500 Internal Server Error
//
//	ErrorResponse - Error retrieving groups
//
// Example Request:
//
//	GET /api/v1/meshcore/groups
//
// Example Response:
//
//	[
//	  {
//	    "id": 5,
//	    "name": "General Chat",
//	    "secret": "0123456789abcdef0123456789abcdef"
//	  },
//	  {
//	    "id": 7,
//	    "name": "Emergency",
//	    "secret": "fedcba9876543210fedcba9876543210"
//	  }
//	]
func (s *Server) handleGetMeshCoreGroups(c echo.Context) error {
	groups, err := schema.GetMeshCoreGroups(c.Request().Context())
	if err != nil {
		return s.apiError(c, err)
	}
	return c.JSON(http.StatusOK, groups)
}

// handleGetMeshCoreNodes returns a list of MeshCore network nodes.
//
// Endpoint: GET /api/v1/meshcore/nodes
//
// Query Parameters:
//   - type (optional): Filter by node type
//   - "chat" - Chat nodes
//   - "room" - Room nodes
//   - "sensor" - Sensor nodes
//   - "repeater" - Repeater nodes (default)
//
// Response: 200 OK
//
//	[]MeshCoreNode - Array of node objects, sorted by last_heard_at (descending)
//
// Response: 500 Internal Server Error
//
//	ErrorResponse - Error retrieving nodes
//
// Example Request:
//
//	GET /api/v1/meshcore/nodes
//	GET /api/v1/meshcore/nodes?type=chat
//
// Example Response:
//
//	[
//	  {
//	    "id": 42,
//	    "packet": [],
//	    "name": "NODE-CHARLIE",
//	    "type": 0,
//	    "prefix": "mc",
//	    "public_key": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
//	    "first_heard_at": "2026-01-15T08:00:00Z",
//	    "last_heard_at": "2026-03-05T14:25:00Z",
//	    "last_latitude": 52.3667,
//	    "last_longitude": 4.8945
//	  }
//	]
func (s *Server) handleGetMeshCoreNodes(c echo.Context) error {
	var (
		nodes []*schema.MeshCoreNode
		err   error
	)

	if kind := c.QueryParam("type"); kind != "" {
		switch kind {
		case "chat":
			nodes, err = schema.GetMeshCoreNodesByType(c.Request().Context(), meshcore.Chat)
		case "room":
			nodes, err = schema.GetMeshCoreNodesByType(c.Request().Context(), meshcore.Room)
		case "sensor":
			nodes, err = schema.GetMeshCoreNodesByType(c.Request().Context(), meshcore.Sensor)
		case "repeater":
			fallthrough
		default:
			nodes, err = schema.GetMeshCoreNodesByType(c.Request().Context(), meshcore.Repeater)
		}
	} else {
		nodes, err = schema.GetMeshCoreNodes(c.Request().Context())
	}

	if err != nil {
		return s.apiError(c, err)
	}

	return c.JSON(http.StatusOK, nodes)
}

// handleGetMeshCoreNodesCloseTo returns nodes within a specified radius of a reference node.
//
// Endpoint: GET /api/v1/meshcore/nodes/close-to/:publickey
//
// Path Parameters:
//   - publickey: Hex-encoded Ed25519 public key (64 characters) of the reference node
//
// Query Parameters:
//   - radius (optional): Search radius in meters (default: 25000 = 25 km)
//
// Response: 200 OK
//
//	NodesCloseToResponse - Object containing the reference node and nearby nodes
//	{
//	  "node": MeshCoreNode,      // The reference node
//	  "nodes": []MeshCoreNode    // Nearby nodes, sorted by distance (ascending)
//	                             // Each node has the "distance" field populated (in meters)
//	}
//
// Response: 404 Not Found
//
//	null - Node not found or has no location data
//
// Response: 400 Bad Request
//
//	ErrorResponse - Invalid radius parameter
//
// Response: 500 Internal Server Error
//
//	ErrorResponse - Error performing proximity query
//
// Example Request:
//
//	GET /api/v1/meshcore/nodes/close-to/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
//	GET /api/v1/meshcore/nodes/close-to/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef?radius=50000
//
// Example Response:
//
//	{
//	  "node": {
//	    "id": 42,
//	    "name": "NODE-CHARLIE",
//	    "type": 0,
//	    "prefix": "mc",
//	    "public_key": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
//	    "first_heard_at": "2026-01-15T08:00:00Z",
//	    "last_heard_at": "2026-03-05T14:25:00Z",
//	    "last_latitude": 52.3667,
//	    "last_longitude": 4.8945,
//	    "distance": 0
//	  },
//	  "nodes": [
//	    {
//	      "id": 43,
//	      "name": "NODE-DELTA",
//	      "type": 0,
//	      "prefix": "mc",
//	      "public_key": "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
//	      "first_heard_at": "2026-02-01T10:00:00Z",
//	      "last_heard_at": "2026-03-05T14:20:00Z",
//	      "last_latitude": 52.3700,
//	      "last_longitude": 4.9000,
//	      "distance": 450.5
//	    }
//	  ]
//	}
func (s *Server) handleGetMeshCoreNodesCloseTo(c echo.Context) error {
	var radius float64
	if value := c.QueryParam("radius"); value != "" {
		var err error
		if radius, err = strconv.ParseFloat(value, 64); err != nil {
			return s.apiError(c, err)
		}
	}
	if radius <= 0 {
		radius = 25000.0 // 25 km
	}

	node, nodes, err := schema.GetMeshCoreNodesCloseTo(c.Request().Context(), c.Param("publickey"), radius)
	if err != nil {
		return s.apiError(c, err)
	}

	return c.JSON(http.StatusOK, map[string]any{
		"node":  node,
		"nodes": nodes,
	})
}

// handleGetMeshCorePackets returns MeshCore packets based on various filter criteria.
//
// Endpoint: GET /api/v1/meshcore/packets
//
// Query Parameters (mutually exclusive, evaluated in order):
//   - hash: 16-character hex hash - Returns all packets with this hash
//   - type: Integer payload type - Returns packets of this type
//   - Can be combined with channel_hash for group messages
//   - channel_hash: 2-character channel hash (requires type parameter)
//   - (no parameters): Returns the 100 most recent packets
//
// Response: 200 OK
//
//	[]MeshCorePacket - Array of packet objects
//
// Response: 400 Bad Request
//
//	ErrorResponse - Invalid hash format or payload type
//
// Response: 500 Internal Server Error
//
//	ErrorResponse - Error retrieving packets
//
// Example Requests:
//
//	GET /api/v1/meshcore/packets
//	GET /api/v1/meshcore/packets?hash=a1b2c3d4e5f67890
//	GET /api/v1/meshcore/packets?type=3
//	GET /api/v1/meshcore/packets?type=3&channel_hash=ab
//
// Example Response:
//
//	[
//	  {
//	    "id": 12345,
//	    "radio_id": 1,
//	    "radio": null,
//	    "snr": 8.5,
//	    "rssi": -95,
//	    "version": 1,
//	    "route_type": 0,
//	    "payload_type": 3,
//	    "hash": "a1b2c3d4e5f67890",
//	    "path": "AQIDBA==",
//	    "payload": "SGVsbG8gV29ybGQ=",
//	    "raw": "AQIDBAUGBwg=",
//	    "parsed": {"text": "Hello World"},
//	    "channel_hash": "ab",
//	    "received_at": "2026-03-05T14:30:00Z"
//	  }
//	]
//
// Payload Types:
//   - 0: Ping
//   - 1: Node announcement
//   - 2: Direct text message
//   - 3: Group text message
//   - 4: Position update
//   - (other types may be defined by the protocol)
func (s *Server) handleGetMeshCorePackets(c echo.Context) error {
	var (
		ctx     = c.Request().Context()
		packets []*schema.MeshCorePacket
		err     error
	)

	if hash := c.QueryParam("hash"); hash != "" {
		packets, err = schema.GetMeshCorePacketsByHash(ctx, hash)
	} else if kind := c.QueryParam("type"); kind != "" {
		var payloadType int
		if payloadType, err = strconv.Atoi(kind); err == nil {
			if hash := c.QueryParam("channel_hash"); hash != "" {
				packets, err = schema.GetMeshCorePacketsByChannelHash(ctx, hash)
			} else {
				packets, err = schema.GetMeshCorePacketsByPayloadType(ctx, meshcore.PayloadType(payloadType))
			}
		}
	} else {
		packets, err = schema.GetMeshCorePackets(ctx, 100)
	}

	if err != nil {
		return s.apiError(c, err)
	}

	return c.JSON(http.StatusOK, packets)
}
