package tools

import (
	"context"
	"fmt"
	"os"

	"github.com/ClareAI/astra-insight-mcp/internal/gateway"
	"github.com/ClareAI/astra-insight-mcp/internal/mcp"
	"github.com/ClareAI/astra-insight-mcp/internal/oauth"
)

func registerListAgents(s *mcp.Server, gw *gateway.Client) {
	tool := mcp.Tool{
		Name:        "list_agents",
		Description: "List all agents for the authenticated tenant. Returns agent names, IDs, types, statuses, and channel types. Use this as the starting point to discover available agents.",
		InputSchema: mcp.InputSchema{
			Type:       "object",
			Properties: map[string]mcp.Property{},
			Required:   []string{},
		},
	}

	s.RegisterTool(tool, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		if apiKey == "" {
			return nil, fmt.Errorf("api_key is required")
		}
		return gw.ListAgents(ctx, apiKey, gateway.AgentListOpts{})
	})
}

func registerGetAgent(s *mcp.Server, gw *gateway.Client) {
	tool := mcp.Tool{
		Name:        "get_agent",
		Description: "Get the agent's current configuration including name, instructions, system rules, status, and channel type. Use this as the first step to understand the current agent setup before making changes.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id": {Type: "string", Description: "Agent ID"},
			},
			Required: []string{"agent_id"},
		},
	}

	s.RegisterTool(tool, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		return gw.GetAgent(ctx, apiKey, agentID)
	})
}

func registerCreateAgent(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "create_agent",
		Description: "Create a new AI agent. IMPORTANT: knowledge_base_id can ONLY be set at creation time — it cannot be changed later via update_agent. Always create the KB first (create_knowledge_base), then pass its ID here. Optionally provide a template_id to initialize from a pre-built template.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"name":               {Type: "string", Description: "Agent name (required)"},
				"description":        {Type: "string", Description: "Agent description"},
				"type":               {Type: "string", Description: "Agent type", Enum: []string{"faq", "conversational"}},
				"channel_type":       {Type: "string", Description: "Channel type", Enum: []string{"web_widget", "whatsapp", "voice"}},
				"communication_mode": {Type: "string", Description: "Communication mode: text, voice, or both", Enum: []string{"text", "voice", "both"}},
				"trigger_type":       {Type: "string", Description: "How the agent is triggered", Enum: []string{"manual", "automatic", "conversation_initiated", "keyword", "schedule", "webhook", "customer_inquiry"}},
				"instructions":       {Type: "string", Description: "Agent instructions / system prompt"},
				"system_rules":       {Type: "string", Description: "System-level rules the agent must follow"},
				"knowledge_base_id":  {Type: "string", Description: "Knowledge base ID to attach"},
				"template_id":        {Type: "string", Description: "Template ID to initialize from"},
			},
			Required: []string{"name"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		name, _ := args["name"].(string)
		if apiKey == "" || name == "" {
			return nil, fmt.Errorf("api_key and name are required")
		}
		req := gateway.AgentCreateRequest{
			Name:              name,
			Description:       strval(args, "description"),
			Type:              strval(args, "type"),
			ChannelType:       strval(args, "channel_type"),
			CommunicationMode: strval(args, "communication_mode"),
			TriggerType:       strval(args, "trigger_type"),
			Instructions:      strval(args, "instructions"),
			SystemRules:       strval(args, "system_rules"),
			KnowledgeBaseID:   strval(args, "knowledge_base_id"),
			TemplateID:        strval(args, "template_id"),
		}
		return gw.CreateAgent(ctx, apiKey, req)
	})
}

func registerUpdateAgent(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "update_agent",
		Description: "Update agent attributes like name, description, instructions, system rules, enabled state, or trigger type. Only provided fields are updated. NOTE: knowledge_base_id CANNOT be changed after creation — it must be set when calling create_agent.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id":     {Type: "string", Description: "Agent ID"},
				"name":         {Type: "string", Description: "Agent name"},
				"description":  {Type: "string", Description: "Agent description"},
				"instructions": {Type: "string", Description: "Agent instructions / system prompt"},
				"system_rules": {Type: "string", Description: "System-level rules"},
				"is_enabled":   {Type: "boolean", Description: "Enable or disable the agent"},
				"trigger_type": {Type: "string", Description: "How the agent is triggered", Enum: []string{"manual", "automatic", "conversation_initiated", "keyword", "schedule", "webhook", "customer_inquiry"}},
			},
			Required: []string{"agent_id"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		req := gateway.AgentUpdateRequest{
			Name:         strval(args, "name"),
			Description:  strval(args, "description"),
			Instructions: strval(args, "instructions"),
			SystemRules:  strval(args, "system_rules"),
			TriggerType:  strval(args, "trigger_type"),
		}
		if v, ok := args["is_enabled"]; ok {
			if b, ok := v.(bool); ok {
				req.IsEnabled = &b
			}
		}
		return gw.UpdateAgent(ctx, apiKey, agentID, req)
	})
}

func registerDeleteAgent(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "delete_agent",
		Description: "Permanently delete an agent. This cannot be undone. Use with caution.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id": {Type: "string", Description: "Agent ID to delete"},
			},
			Required: []string{"agent_id"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		if err := gw.DeleteAgent(ctx, apiKey, agentID); err != nil {
			return nil, err
		}
		return map[string]string{"status": "deleted", "agent_id": agentID}, nil
	})
}

func registerUpdateInstruction(s *mcp.Server, gw *gateway.Client) {
	tool := mcp.Tool{
		Name:        "update_agent_instruction",
		Description: "Update the agent's instructions. This modifies the draft instruction — call publish_agent afterwards to make it live. Use this after analyzing conversations and analytics to improve agent behavior.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id":     {Type: "string", Description: "Agent ID"},
				"instructions": {Type: "string", Description: "New instruction text for the agent"},
			},
			Required: []string{"agent_id", "instructions"},
		},
	}

	s.RegisterTool(tool, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		instructions, _ := args["instructions"].(string)
		if apiKey == "" || agentID == "" || instructions == "" {
			return nil, fmt.Errorf("api_key, agent_id, and instructions are required")
		}

		return gw.UpdateInstruction(ctx, apiKey, agentID, gateway.UpdateInstructionRequest{
			Instruction: instructions,
		})
	})
}

func registerPublishAgent(s *mcp.Server, gw *gateway.Client) {
	tool := mcp.Tool{
		Name:        "publish_agent",
		Description: "Publish the agent's draft changes to make them live. Call this after updating instructions and verifying with test messages. This activates the new configuration for real users.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id": {Type: "string", Description: "Agent ID"},
			},
			Required: []string{"agent_id"},
		},
	}

	s.RegisterTool(tool, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		return gw.PublishAgent(ctx, apiKey, agentID)
	})
}

func registerCloneAgent(s *mcp.Server, gw *gateway.Client) {
	tool := mcp.Tool{
		Name:        "clone_agent",
		Description: "Clone an existing agent to create a copy. Use this to create a safe copy for experimenting with changes before modifying the original.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id": {Type: "string", Description: "Agent ID to clone"},
			},
			Required: []string{"agent_id"},
		},
	}

	s.RegisterTool(tool, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		return gw.CloneAgent(ctx, apiKey, agentID, nil)
	})
}

func registerGetRuntimeConfig(s *mcp.Server, gw *gateway.Client) {
	tool := mcp.Tool{
		Name:        "get_runtime_config",
		Description: "Get the agent's runtime configuration — the actual deployed instructions, system rules, actions, and tool definitions. Shows what is currently live (active) or in draft. Use this to compare draft vs live configurations.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id": {Type: "string", Description: "Agent ID"},
				"status":   {Type: "string", Description: "Config status: active (live), draft, or test", Enum: []string{"active", "draft", "test"}},
			},
			Required: []string{"agent_id"},
		},
	}

	s.RegisterTool(tool, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		status, _ := args["status"].(string)
		return gw.GetRuntimeConfig(ctx, apiKey, agentID, status)
	})
}

func registerGetBrandkit(s *mcp.Server, gw *gateway.Client) {
	tool := mcp.Tool{
		Name:        "get_brandkit",
		Description: "Get the agent's brand kit — brand colors, logos, company info, and appearance settings. Use this to understand the brand context for the agent.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id": {Type: "string", Description: "Agent ID"},
			},
			Required: []string{"agent_id"},
		},
	}

	s.RegisterTool(tool, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		return gw.GetBrandkit(ctx, apiKey, agentID)
	})
}

func registerFetchBrandkit(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "fetch_brandkit",
		Description: "Auto-generate a brand kit from a company domain or URL. Uses Brandfetch + AI to produce logos, colors, fonts, persona, and appearance settings. Provide either 'identifier' (domain/URL) or 'description' for AI-only generation.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"identifier":  {Type: "string", Description: "Company domain or URL (e.g. wati.io). Protocol and www. prefix are stripped automatically."},
				"agent_id":    {Type: "string", Description: "Optional agent ID to associate the brandkit with"},
				"description": {Type: "string", Description: "Free-text company description for AI-only generation when identifier is not provided"},
			},
			Required: []string{},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		if apiKey == "" {
			return nil, fmt.Errorf("api_key is required")
		}
		req := gateway.FetchBrandkitRequest{
			Identifier:  strval(args, "identifier"),
			AgentID:     strval(args, "agent_id"),
			Description: strval(args, "description"),
		}
		if req.Identifier == "" && req.Description == "" {
			return nil, fmt.Errorf("either identifier or description is required")
		}
		return gw.FetchBrandkit(ctx, apiKey, req)
	})
}

func registerCreateBrandkit(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "create_brandkit",
		Description: "Manually create a brand kit for an agent by providing brand assets directly. Use this when you already have brand data (as opposed to fetch_brandkit which auto-generates from a URL).",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id":               {Type: "string", Description: "Agent ID"},
				"brand_name":             {Type: "string", Description: "Brand/company name"},
				"domain":                 {Type: "string", Description: "Company domain"},
				"brand_description":      {Type: "string", Description: "Short brand description"},
				"persona":                {Type: "string", Description: "Brand persona / tone of voice"},
				"chat_placeholder":       {Type: "string", Description: "Placeholder text for the chat input"},
				"minimized_button_label": {Type: "string", Description: "Label for the minimized chat button"},
				"logos":                  {Type: "array", Description: "Array of logo objects with 'type' and 'url' fields"},
				"colors":                 {Type: "array", Description: "Array of color objects with 'type' and 'hex' fields"},
				"fonts":                  {Type: "array", Description: "Array of font objects with 'name' and 'type' fields"},
				"appearance_config":      {Type: "object", Description: "Appearance configuration (free-form)"},
				"sales_config":           {Type: "object", Description: "Sales configuration (free-form)"},
			},
			Required: []string{"agent_id"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		req := parseBrandkitUpdate(args)
		return gw.CreateBrandkit(ctx, apiKey, agentID, req)
	})
}

func registerUpdateBrandkit(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "update_brandkit",
		Description: "Update an existing brand kit for an agent. Only provided fields are updated; omitted fields remain unchanged.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id":               {Type: "string", Description: "Agent ID"},
				"brand_name":             {Type: "string", Description: "Brand/company name"},
				"domain":                 {Type: "string", Description: "Company domain"},
				"brand_description":      {Type: "string", Description: "Short brand description"},
				"persona":                {Type: "string", Description: "Brand persona / tone of voice"},
				"chat_placeholder":       {Type: "string", Description: "Placeholder text for the chat input"},
				"minimized_button_label": {Type: "string", Description: "Label for the minimized chat button"},
				"logos":                  {Type: "array", Description: "Array of logo objects with 'type' and 'url' fields"},
				"colors":                 {Type: "array", Description: "Array of color objects with 'type' and 'hex' fields"},
				"fonts":                  {Type: "array", Description: "Array of font objects with 'name' and 'type' fields"},
				"appearance_config":      {Type: "object", Description: "Appearance configuration (free-form)"},
				"sales_config":           {Type: "object", Description: "Sales configuration (free-form)"},
			},
			Required: []string{"agent_id"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		req := parseBrandkitUpdate(args)
		return gw.UpdateBrandkit(ctx, apiKey, agentID, req)
	})
}

func registerDeleteBrandkit(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "delete_brandkit",
		Description: "Permanently delete the brand kit associated with an agent.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id": {Type: "string", Description: "Agent ID"},
			},
			Required: []string{"agent_id"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		if apiKey == "" || agentID == "" {
			return nil, fmt.Errorf("api_key and agent_id are required")
		}
		return gw.DeleteBrandkit(ctx, apiKey, agentID)
	})
}

func parseBrandkitUpdate(args map[string]interface{}) gateway.BrandkitUpdate {
	req := gateway.BrandkitUpdate{
		BrandName:        strval(args, "brand_name"),
		Domain:           strval(args, "domain"),
		BrandDescription: strval(args, "brand_description"),
		Persona:          strval(args, "persona"),
		ChatPlaceholder:  strval(args, "chat_placeholder"),
	}
	if label := strval(args, "minimized_button_label"); label != "" {
		req.MinimizedButtonLabel = &gateway.MinimizedButtonLabel{Enabled: true, Label: label}
	}
	if v, ok := args["logos"].([]interface{}); ok {
		for _, item := range v {
			if m, ok := item.(map[string]interface{}); ok {
				entry := map[string]string{}
				for k, val := range m {
					if s, ok := val.(string); ok {
						entry[k] = s
					}
				}
				req.Logos = append(req.Logos, entry)
			}
		}
	}
	if v, ok := args["colors"].([]interface{}); ok {
		for _, item := range v {
			if m, ok := item.(map[string]interface{}); ok {
				entry := map[string]string{}
				for k, val := range m {
					if s, ok := val.(string); ok {
						entry[k] = s
					}
				}
				req.Colors = append(req.Colors, entry)
			}
		}
	}
	if v, ok := args["fonts"].([]interface{}); ok {
		for _, item := range v {
			if m, ok := item.(map[string]interface{}); ok {
				entry := map[string]string{}
				for k, val := range m {
					if s, ok := val.(string); ok {
						entry[k] = s
					}
				}
				req.Fonts = append(req.Fonts, entry)
			}
		}
	}
	if v, ok := args["appearance_config"].(map[string]interface{}); ok {
		req.AppearanceConfig = v
	}
	if v, ok := args["sales_config"].(map[string]interface{}); ok {
		req.SalesConfig = v
	}
	return req
}

func strval(args map[string]interface{}, key string) string {
	v, _ := args[key].(string)
	return v
}

// apiKeyFromArgs returns the api_key from tool args, falling back to ASTRA_API_KEY env var.
func apiKeyFromArgs(args map[string]interface{}) string {
	if v, _ := args["api_key"].(string); v != "" {
		return v
	}
	return resolveAuthToken()
}

// resolveAuthToken checks env vars and token file in priority order.
// For OAuth tokens from file, it auto-refreshes if expired.
func resolveAuthToken() string {
	for _, key := range []string{"ASTRA_API_KEY", "ASTRA_ACCESS_TOKEN", "ASTRA_OAUTH_ACCESS_TOKEN"} {
		if v := os.Getenv(key); v != "" {
			return v
		}
	}
	return oauth.EnsureValidToken()
}
