package tools

import (
	"context"
	"fmt"

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

// registerIntegrationSkill registers all Composio MCP / integration tools.
func registerIntegrationSkill(s *mcp.Server, gw *gateway.Client) {
	registerGetIntegrationPlatforms(s, gw)
	registerListIntegrationTemplates(s, gw)
	registerListAgentIntegrations(s, gw)
	registerListConfiguredIntegrations(s, gw)
	registerGetConfiguredIntegration(s, gw)
	registerCreateIntegration(s, gw)
	registerDeleteIntegration(s, gw)
	registerCreateIntegrationAuth(s, gw)
	registerWaitIntegrationAuth(s, gw)
	registerGetIntegrationAuth(s, gw)
	registerCreateConnection(s, gw)
	registerGetWatiSettings(s, gw)
	registerRegisterAgentIntegrations(s, gw)
	registerDeregisterAgentIntegrations(s, gw)
}

func registerGetIntegrationPlatforms(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "get_integration_platforms",
		Description: "List all available integration platforms and their tools (e.g. Slack, HubSpot, Google Sheets, Salesforce, Zoho, PerplexityAI). Returns a map of platform → tool names. Use this to discover what integrations can be added to an agent.",
		InputSchema: mcp.InputSchema{
			Type:       "object",
			Properties: map[string]mcp.Property{},
			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")
		}
		return gw.GetMCPInfo(ctx, apiKey)
	})
}

func registerListIntegrationTemplates(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "list_integration_templates",
		Description: "List all available action templates — pre-built integration blueprints for platforms like Slack, HubSpot, etc. Each template shows: at_id, at_name, platform, mcp_tool, need_auth, allowed_agent_types, allowed_modalities, categories. Use at_id when creating a new integration.",
		InputSchema: mcp.InputSchema{
			Type:       "object",
			Properties: map[string]mcp.Property{},
			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")
		}
		return gw.ListMCPActionTemplates(ctx, apiKey)
	})
}

func registerListAgentIntegrations(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "list_agent_integrations",
		Description: "List the MCP tools currently registered for a specific agent — the actual integration tools it can use at runtime (e.g. SLACK_SEND_MESSAGE, HUBSPOT_CREATE_CONTACT). Uses the MCP JSON-RPC tools/list protocol.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id": {Type: "string", Description: "Agent ID"},
				"mode":     {Type: "string", Description: "Agent config mode", Enum: []string{"published", "draft"}},
				"modality": {Type: "string", Description: "Modality filter", Enum: []string{"text", "voice", "voice_inbound", "voice_outbound"}},
			},
			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")
		}
		mode := strval(args, "mode")
		modality := strval(args, "modality")
		return gw.ListMCPAgentTools(ctx, apiKey, agentID, mode, modality)
	})
}

func registerListConfiguredIntegrations(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "list_configured_integrations",
		Description: "List all integration actions configured for the tenant — shows action_name, at_id, at_name, platform, connection_id, status. These are the tenant-level action configs that can be registered to agents.",
		InputSchema: mcp.InputSchema{
			Type:       "object",
			Properties: map[string]mcp.Property{},
			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")
		}
		return gw.ListMCPActions(ctx, apiKey)
	})
}

func registerGetConfiguredIntegration(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "get_configured_integration",
		Description: "Get details of a specific configured integration action — action_name, at_id, platform, connection_id, metadata, status.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"action_id": {Type: "string", Description: "MCP Action ID"},
			},
			Required: []string{"action_id"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		actionID, _ := args["action_id"].(string)
		if apiKey == "" || actionID == "" {
			return nil, fmt.Errorf("api_key and action_id are required")
		}
		return gw.GetMCPAction(ctx, apiKey, actionID)
	})
}

func registerCreateIntegration(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "create_integration",
		Description: "Create a new integration action from a template. Provide the at_id (from list_integration_templates), a custom name, status, and optionally a connection_id if the template requires auth (need_auth=true). After creation, register it to an agent with register_agent_integrations.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"action_name":   {Type: "string", Description: "Custom name for this action (e.g. 'Send Slack Alert')"},
				"action_intent": {Type: "string", Description: "Description of what this action does"},
				"at_id":         {Type: "string", Description: "Action template ID from list_integration_templates"},
				"connection_id": {Type: "string", Description: "OAuth connection ID (required if template has need_auth=true)"},
				"status":        {Type: "string", Description: "Action status", Enum: []string{"draft", "active"}},
				"metadata":      {Type: "object", Description: "Additional metadata in JSON format"},
				"platform":      {Type: "string", Description: "Platform identifier (only for webhook actions with oauth2 auth_type)"},
			},
			Required: []string{"action_name", "at_id", "status"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		actionName, _ := args["action_name"].(string)
		atID, _ := args["at_id"].(string)
		status, _ := args["status"].(string)
		if apiKey == "" || actionName == "" || atID == "" || status == "" {
			return nil, fmt.Errorf("api_key, action_name, at_id, and status are required")
		}
		tenantID, err := resolveTenantID(ctx, gw, apiKey)
		if err != nil {
			return nil, err
		}
		var metadata map[string]interface{}
		if m, ok := args["metadata"].(map[string]interface{}); ok {
			metadata = m
		}
		req := gateway.MCPActionCreateRequest{
			ActionName:   actionName,
			ActionIntent: strval(args, "action_intent"),
			AtID:         atID,
			ConnectionID: strval(args, "connection_id"),
			Status:       status,
			TenantID:     tenantID,
			Metadata:     metadata,
			Platform:     strval(args, "platform"),
		}
		return gw.CreateMCPAction(ctx, apiKey, req)
	})
}

func registerDeleteIntegration(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "delete_integration",
		Description: "Delete an existing integration action. This removes it from the tenant — make sure to deregister it from agents first.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"action_id": {Type: "string", Description: "MCP Action ID to delete"},
			},
			Required: []string{"action_id"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		actionID, _ := args["action_id"].(string)
		if apiKey == "" || actionID == "" {
			return nil, fmt.Errorf("api_key and action_id are required")
		}
		return gw.DeleteMCPAction(ctx, apiKey, actionID)
	})
}

func registerCreateIntegrationAuth(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "create_integration_auth",
		Description: "Initiate an OAuth connection for a platform (e.g. slack, hubspot, salesforce, googlesheets, zoho). Returns a connection_id and auth URL the user needs to visit to authorize. After auth completes, use the connection_id when creating integrations. For WATI platform, provide bearer_token and api_endpoint instead of OAuth.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"platform":     {Type: "string", Description: "Platform name (e.g. slack, hubspot, salesforce, googlesheets, zoho, wati)"},
				"bearer_token": {Type: "string", Description: "Bearer token (required for WATI platform only)"},
				"api_endpoint": {Type: "string", Description: "API endpoint URL (required for WATI platform only)"},
			},
			Required: []string{"platform"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		platform, _ := args["platform"].(string)
		if apiKey == "" || platform == "" {
			return nil, fmt.Errorf("api_key and platform are required")
		}
		tenantID, err := resolveTenantID(ctx, gw, apiKey)
		if err != nil {
			return nil, err
		}
		req := gateway.MCPAuthRequest{
			Platform:    platform,
			TenantID:    tenantID,
			BearerToken: strval(args, "bearer_token"),
			APIEndpoint: strval(args, "api_endpoint"),
		}
		return gw.CreateMCPAuth(ctx, apiKey, req)
	})
}

func registerWaitIntegrationAuth(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "wait_integration_auth",
		Description: "Wait for a user to complete OAuth authentication. Call this after create_integration_auth — it blocks until the user finishes auth or the timeout expires. On success returns account_name; use it together with the connection_id to call create_connection.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"connection_id": {Type: "string", Description: "Connection ID from create_integration_auth"},
				"timeout":       {Type: "integer", Description: "Timeout in seconds (default 120)"},
				"platform":      {Type: "string", Description: "Platform name (optional, e.g. hubspot)"},
			},
			Required: []string{"connection_id"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		connID, _ := args["connection_id"].(string)
		if apiKey == "" || connID == "" {
			return nil, fmt.Errorf("api_key and connection_id are required")
		}
		timeout := 120
		if t, ok := args["timeout"].(float64); ok && t > 0 {
			timeout = int(t)
		}
		req := gateway.MCPWaitAuthRequest{
			Timeout:  timeout,
			Platform: strval(args, "platform"),
		}
		return gw.WaitMCPAuth(ctx, apiKey, connID, req)
	})
}

func registerGetIntegrationAuth(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "get_integration_auth",
		Description: "Check the status of an OAuth connection — whether it's pending, connected, or expired.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"connection_id": {Type: "string", Description: "Connection ID from create_integration_auth"},
			},
			Required: []string{"connection_id"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		connID, _ := args["connection_id"].(string)
		if apiKey == "" || connID == "" {
			return nil, fmt.Errorf("api_key and connection_id are required")
		}
		return gw.GetMCPAuth(ctx, apiKey, connID)
	})
}

func registerCreateConnection(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "create_connection",
		Description: "Create a platform connection mapping after OAuth auth succeeds. This is the final step: call create_integration_auth → wait_integration_auth → create_connection. Requires the connection_id from step 1, the account_name from step 2 (passed as connection_name), plus platform.",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"platform":        {Type: "string", Description: "Platform name (e.g. hubspot, slack, salesforce)"},
				"connection_id":   {Type: "string", Description: "Connection ID from create_integration_auth"},
				"connection_name": {Type: "string", Description: "Connection name — use the account_name returned by wait_integration_auth"},
			},
			Required: []string{"platform", "connection_id", "connection_name"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		platform, _ := args["platform"].(string)
		connID, _ := args["connection_id"].(string)
		connName, _ := args["connection_name"].(string)
		if apiKey == "" || platform == "" || connID == "" || connName == "" {
			return nil, fmt.Errorf("api_key, platform, connection_id, and connection_name are required")
		}
		tenantID, err := resolveTenantID(ctx, gw, apiKey)
		if err != nil {
			return nil, err
		}
		req := gateway.ConnectionCreateRequest{
			Platform:       platform,
			ConnectionID:   connID,
			ConnectionName: connName,
			TenantID:       tenantID,
		}
		return gw.CreateConnection(ctx, apiKey, req)
	})
}

func registerRegisterAgentIntegrations(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "register_agent_integrations",
		Description: "Register integration tools with an agent — this makes the tools available for the agent to use at runtime. Provide the agent_id, mode (draft/published), and a list of actions (each with action_id and at_id from list_configured_integrations).",
		InputSchema: mcp.InputSchema{
			Type: "object",
			Properties: map[string]mcp.Property{
				"agent_id": {Type: "string", Description: "Agent ID"},
				"mode":     {Type: "string", Description: "Config mode", Enum: []string{"draft", "published"}},
				"actions":  {Type: "array", Description: "Array of {action_id, at_id} objects to register"},
				"modality": {Type: "string", Description: "Modality", Enum: []string{"text", "voice", "voice_inbound", "voice_outbound"}},
			},
			Required: []string{"agent_id", "mode", "actions"},
		},
	}, func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
		apiKey := apiKeyFromArgs(args)
		agentID, _ := args["agent_id"].(string)
		mode, _ := args["mode"].(string)
		if apiKey == "" || agentID == "" || mode == "" {
			return nil, fmt.Errorf("api_key, agent_id, and mode are required")
		}
		tenantID, err := resolveTenantID(ctx, gw, apiKey)
		if err != nil {
			return nil, err
		}

		actions, err := parseMCPRegisterActions(args["actions"])
		if err != nil {
			return nil, err
		}

		req := gateway.MCPRegisterRequest{
			AgentID:  agentID,
			Mode:     mode,
			Actions:  actions,
			TenantID: tenantID,
			Modality: strval(args, "modality"),
		}
		return gw.RegisterMCPTools(ctx, apiKey, req)
	})
}

func registerDeregisterAgentIntegrations(s *mcp.Server, gw *gateway.Client) {
	s.RegisterTool(mcp.Tool{
		Name:        "deregister_agent_integrations",
		Description: "Remove ALL integration tools from an agent — deregisters every MCP configuration for this agent. Typically used when deleting 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")
		}
		tenantID, err := resolveTenantID(ctx, gw, apiKey)
		if err != nil {
			return nil, err
		}
		req := gateway.MCPDeregisterRequest{
			AgentID:  agentID,
			TenantID: tenantID,
		}
		return gw.DeregisterMCPTools(ctx, apiKey, req)
	})
}

// resolveTenantID extracts the tenant_id for the given API key via the gateway.
// The composio-mcp POST endpoints require tenant_id in the request body.
func resolveTenantID(ctx context.Context, gw *gateway.Client, apiKey string) (string, error) {
	tid, err := gw.ResolveTenantID(ctx, apiKey)
	if err != nil {
		return "", fmt.Errorf("could not resolve tenant_id: %w", err)
	}
	return tid, nil
}

// parseMCPRegisterActions converts the raw LLM output into typed register actions.
func parseMCPRegisterActions(raw interface{}) ([]gateway.MCPRegisterAction, error) {
	arr, ok := raw.([]interface{})
	if !ok {
		return nil, fmt.Errorf("actions must be an array of {action_id, at_id} objects")
	}
	var result []gateway.MCPRegisterAction
	for _, item := range arr {
		m, ok := item.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("each action must be an object with action_id and at_id")
		}
		actionID, _ := m["action_id"].(string)
		atID, _ := m["at_id"].(string)
		if actionID == "" || atID == "" {
			return nil, fmt.Errorf("each action requires both action_id and at_id")
		}
		result = append(result, gateway.MCPRegisterAction{
			ActionID: actionID,
			AtID:     atID,
		})
	}
	return result, nil
}
