package tools

import (
	"context"
	"encoding/json"
)

// ── Tool Definition (sent to LLM) ──────────

// ToolDef describes a tool the LLM can call. Serialized to OpenAI-compatible
// format by providers. The Parameters field uses JSON Schema.
type ToolDef struct {
	Name        string          `json:"name"`
	DisplayName string          `json:"display_name,omitempty"` // human-readable label for toggle UI
	Description string          `json:"description"`
	Parameters  json.RawMessage `json:"parameters"`  // JSON Schema object
	Category    string          `json:"category,omitempty"` // UI grouping: search, notes, utilities, browser
}

// ── Tool Call (from LLM response) ───────────

// ToolCall represents a single tool invocation requested by the LLM.
type ToolCall struct {
	ID        string `json:"id"`                  // provider-assigned call ID
	Name      string `json:"name"`                // tool function name
	Arguments string `json:"arguments"`           // JSON string of arguments
}

// ── Tool Result (fed back to LLM) ───────────

// ToolResult is the output of executing a tool, sent back to the LLM
// as a message with role "tool".
type ToolResult struct {
	ToolCallID string `json:"tool_call_id"`
	Name       string `json:"name"`
	Content    string `json:"content"`             // JSON-encoded result
	IsError    bool   `json:"is_error,omitempty"`  // true if execution failed
}

// ── Tool Interface ──────────────────────────

// ExecutionContext provides tool implementations with the info they need
// at execution time. Populated from channel/request state in the
// completion handler.
type ExecutionContext struct {
	UserID      string
	ChannelID   string
	PersonaID   string // set when a persona is active — tools use for scoped KB access
	WorkspaceID string // set when channel/project has a workspace bound
	WorkflowID  string // v0.25.0: set when channel is a workflow instance
	TeamID      string // v0.25.0: set when channel belongs to a team
}

// Tool is the interface every built-in tool implements.
type Tool interface {
	// Definition returns the tool's schema for the LLM.
	Definition() ToolDef

	// Execute runs the tool with the given JSON arguments.
	// Returns a JSON string result or an error.
	Execute(ctx context.Context, execCtx ExecutionContext, argsJSON string) (string, error)
}

// ── Context-Aware Tool System (v0.25.0) ────

// ToolContext describes the runtime environment for tool availability
// checks. Built from channel metadata in the completion handler —
// separate from ExecutionContext because availability is checked before
// execution.
type ToolContext struct {
	ChannelType string // "direct", "dm", "group", "channel", "workflow"
	WorkspaceID string // non-empty when channel/project has a workspace
	WorkflowID  string // non-empty when channel is a workflow instance
	TeamID      string // non-empty when channel belongs to a team
	PersonaID   string // non-empty when a persona is active
	IsVisitor   bool   // true for session_participants (v0.24.3)
}

// Require is a predicate that determines if a tool is available in a
// given context. Return true = tool is available, false = suppressed.
type Require func(ToolContext) bool

// ContextualTool extends Tool with an availability predicate. Tools
// that embed BaseTool get AlwaysAvailable by default. Tools that need
// context-dependent visibility override Availability().
type ContextualTool interface {
	Tool
	Availability() Require
}

// BaseTool provides default availability (always available). Embed in
// any tool struct for backward compat — no behavioral change.
type BaseTool struct{}

// Availability returns AlwaysAvailable. Override in tool structs that
// need context-dependent visibility.
func (BaseTool) Availability() Require { return AlwaysAvailable }

// AlwaysAvailable is the default predicate — tool is available in
// every context. Defined here (not in predicates.go) because BaseTool
// references it.
func AlwaysAvailable(_ ToolContext) bool { return true }

// ── Helpers ─────────────────────────────────

// MustJSON marshals v to a json.RawMessage, panicking on error.
// Used for static parameter schemas at init time.
func MustJSON(v interface{}) json.RawMessage {
	b, err := json.Marshal(v)
	if err != nil {
		panic("tools.MustJSON: " + err.Error())
	}
	return json.RawMessage(b)
}

// JSONSchema builds a JSON Schema object type with the given properties.
// Convenience for defining tool parameters.
func JSONSchema(properties map[string]interface{}, required []string) json.RawMessage {
	schema := map[string]interface{}{
		"type":       "object",
		"properties": properties,
	}
	if len(required) > 0 {
		schema["required"] = required
	}
	return MustJSON(schema)
}

// Prop builds a JSON Schema property definition.
func Prop(typ, description string) map[string]interface{} {
	return map[string]interface{}{
		"type":        typ,
		"description": description,
	}
}

// PropEnum builds a JSON Schema string property with enum values.
func PropEnum(description string, values ...string) map[string]interface{} {
	return map[string]interface{}{
		"type":        "string",
		"description": description,
		"enum":        values,
	}
}

// PropArray builds a JSON Schema array property with string items.
func PropArray(description string) map[string]interface{} {
	return map[string]interface{}{
		"type":        "array",
		"description": description,
		"items":       map[string]string{"type": "string"},
	}
}
