package main

import (
	"crypto/rand"
	"crypto/subtle"
	"encoding/hex"
	"encoding/json"
	"io"
	"net/http"

	"fn-registry/functions/infra"
)

const internalTokenHeader = "X-Internal-Token"

// generateInternalToken returns a 32-byte hex token used by the kanban-mcp
// subprocess to call back into /api/tool/{name}. Generated fresh per process.
func generateInternalToken() string {
	b := make([]byte, 32)
	if _, err := rand.Read(b); err != nil {
		panic("rand.Read: " + err.Error())
	}
	return hex.EncodeToString(b)
}

// handleInternalTool exposes executeTool via HTTP for the MCP subprocess.
// Auth: shared internal token in X-Internal-Token header. Constant-time compare.
func handleInternalTool(db *DB, expectedToken string, logger *ChatLogger) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		got := r.Header.Get(internalTokenHeader)
		if subtle.ConstantTimeCompare([]byte(got), []byte(expectedToken)) != 1 {
			infra.HTTPErrorResponse(w, infra.HTTPError{Status: http.StatusUnauthorized, Code: "unauthorized", Message: "invalid internal token"})
			return
		}
		name := r.PathValue("name")
		if name == "" {
			infra.HTTPErrorResponse(w, infra.HTTPError{Status: http.StatusBadRequest, Code: "bad_request", Message: "tool name required"})
			return
		}
		body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, maxBodyBytes))
		if err != nil {
			infra.HTTPErrorResponse(w, infra.HTTPError{Status: http.StatusBadRequest, Code: "bad_request", Message: err.Error()})
			return
		}
		if len(body) == 0 {
			body = []byte("{}")
		}
		input := json.RawMessage(body)
		if err := validateToolName(name); err != nil {
			infra.HTTPErrorResponse(w, infra.HTTPError{Status: http.StatusNotFound, Code: "unknown_tool", Message: err.Error()})
			return
		}
		res := executeTool(db, name, input)
		if logger != nil {
			logger.Log(name, input, res)
		}
		// Always 200 — MCP-side maps res.OK to MCP isError.
		infra.HTTPJSONResponse(w, http.StatusOK, res)
	}
}
