package cmd

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"

	"skraak/db"
	"skraak/prompts"
	"skraak/resources"
	"skraak/tools"

	"github.com/modelcontextprotocol/go-sdk/mcp"
)

// RunMCP starts the MCP server on stdio transport with all registered tools and resources
func RunMCP(args []string) {
	fs := flag.NewFlagSet("mcp", flag.ExitOnError)
	dbPath := fs.String("db", "", "Path to DuckDB database (required)")

	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: skraak mcp --db <path>\n\n")
		fmt.Fprintf(os.Stderr, "Start the MCP server on stdio transport.\n\n")
		fmt.Fprintf(os.Stderr, "Options:\n")
		fs.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nExamples:\n")
		fmt.Fprintf(os.Stderr, "  skraak mcp --db ./db/skraak.duckdb\n")
		fmt.Fprintf(os.Stderr, "  skraak mcp --db ./db/test.duckdb\n")
	}

	if err := fs.Parse(args); err != nil {
		os.Exit(1)
	}

	if *dbPath == "" {
		fmt.Fprintf(os.Stderr, "Error: --db is required\n\n")
		fs.Usage()
		os.Exit(1)
	}

	tools.SetDBPath(*dbPath)

	// Initialize event log for mutation logging
	eventLogPath := *dbPath + ".events.jsonl"
	db.SetEventLogConfig(db.EventLogConfig{
		Enabled: true,
		Path:    eventLogPath,
	})
	defer db.CloseEventLog()

	// Set schema path for resources package
	schemaPath := filepath.Join(filepath.Dir(os.Args[0]), "db", "schema.sql")
	resources.SetSchemaPath(schemaPath)

	// Create MCP server
	server := mcp.NewServer(&mcp.Implementation{
		Name:    "skraak",
		Version: "v1.0.0",
	}, nil)

	// Register tools (thin adapters that bridge MCP types to core functions)
	mcp.AddTool(server, &mcp.Tool{
		Name:        "get_current_time",
		Description: "Get the current system time with timezone information",
	}, mcpGetCurrentTime)

	mcp.AddTool(server, &mcp.Tool{
		Name:        "execute_sql",
		Description: "Execute arbitrary SQL SELECT queries against the database. Supports parameterized queries with ? placeholders. Database is read-only. Results limited to 1000 rows by default (max 10000). Use with schema resources to construct queries.",
	}, mcpExecuteSQL)

	mcp.AddTool(server, &mcp.Tool{
		Name:        "create_or_update_dataset",
		Description: "Create or update a dataset. Omit 'id' to create (name required), provide 'id' to update. Returns the dataset with timestamps.",
	}, mcpCreateOrUpdateDataset)

	mcp.AddTool(server, &mcp.Tool{
		Name:        "create_or_update_location",
		Description: "Create or update a location. Omit 'id' to create (dataset_id, name, latitude, longitude, timezone_id required), provide 'id' to update. Location must belong to the specified dataset when creating.",
	}, mcpCreateOrUpdateLocation)

	mcp.AddTool(server, &mcp.Tool{
		Name:        "create_or_update_cluster",
		Description: "Create or update a cluster. Omit 'id' to create (dataset_id, location_id, name, sample_rate required), provide 'id' to update. Query existing patterns first with execute_sql before setting cyclic_recording_pattern_id.",
	}, mcpCreateOrUpdateCluster)

	mcp.AddTool(server, &mcp.Tool{
		Name:        "create_or_update_pattern",
		Description: "Create or update a cyclic recording pattern. Omit 'id' to create (record_seconds, sleep_seconds required), provide 'id' to update. Returns existing pattern if duplicate record/sleep values found.",
	}, mcpCreateOrUpdatePattern)

	mcp.AddTool(server, &mcp.Tool{
		Name:        "import_audio_files",
		Description: "Batch import WAV files from a folder into the database. Automatically parses AudioMoth and filename timestamps, calculates hashes, extracts metadata, and computes astronomical data. Files are imported in a single transaction. Duplicate files (by hash) are skipped.",
	}, mcpImportAudioFiles)

	mcp.AddTool(server, &mcp.Tool{
		Name:        "import_ml_selections",
		Description: "Import hand sorted ML-detected kiwi call selections from folder structure organized by species/call-type with WAV/PNG pairs",
	}, mcpImportMLSelections)

	// Register schema resources
	schemaResource, schemaTemplate := resources.GetSchemaResources()
	server.AddResource(schemaResource, resources.SchemaResourceHandler)
	server.AddResourceTemplate(schemaTemplate, resources.SchemaResourceHandler)

	// Register prompts
	server.AddPrompt(prompts.GetQueryDatasetsPrompt(), prompts.QueryDatasetsPromptHandler)
	server.AddPrompt(prompts.GetExploreSchemaPrompt(), prompts.ExploreSchemaPromptHandler)
	server.AddPrompt(prompts.GetExploreLocationHierarchyPrompt(), prompts.ExploreLocationHierarchyPromptHandler)
	server.AddPrompt(prompts.GetQueryLocationDataPrompt(), prompts.QueryLocationDataPromptHandler)
	server.AddPrompt(prompts.GetAnalyzeClusterFilesPrompt(), prompts.AnalyzeClusterFilesPromptHandler)
	server.AddPrompt(prompts.GetSystemStatusPrompt(), prompts.SystemStatusPromptHandler)

	// Run the server on stdio transport
	if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
		log.Fatalf("Server error: %v", err)
	}
}

// MCP adapter functions — thin wrappers that bridge MCP signatures to core tool functions

func mcpGetCurrentTime(ctx context.Context, req *mcp.CallToolRequest, input tools.GetCurrentTimeInput) (*mcp.CallToolResult, tools.GetCurrentTimeOutput, error) {
	output, err := tools.GetCurrentTime(ctx, input)
	return &mcp.CallToolResult{}, output, err
}

func mcpExecuteSQL(ctx context.Context, req *mcp.CallToolRequest, input tools.ExecuteSQLInput) (*mcp.CallToolResult, tools.ExecuteSQLOutput, error) {
	output, err := tools.ExecuteSQL(ctx, input)
	return &mcp.CallToolResult{}, output, err
}

func mcpCreateOrUpdateDataset(ctx context.Context, req *mcp.CallToolRequest, input tools.DatasetInput) (*mcp.CallToolResult, tools.DatasetOutput, error) {
	output, err := tools.CreateOrUpdateDataset(ctx, input)
	return &mcp.CallToolResult{}, output, err
}

func mcpCreateOrUpdateLocation(ctx context.Context, req *mcp.CallToolRequest, input tools.LocationInput) (*mcp.CallToolResult, tools.LocationOutput, error) {
	output, err := tools.CreateOrUpdateLocation(ctx, input)
	return &mcp.CallToolResult{}, output, err
}

func mcpCreateOrUpdateCluster(ctx context.Context, req *mcp.CallToolRequest, input tools.ClusterInput) (*mcp.CallToolResult, tools.ClusterOutput, error) {
	output, err := tools.CreateOrUpdateCluster(ctx, input)
	return &mcp.CallToolResult{}, output, err
}

func mcpCreateOrUpdatePattern(ctx context.Context, req *mcp.CallToolRequest, input tools.PatternInput) (*mcp.CallToolResult, tools.PatternOutput, error) {
	output, err := tools.CreateOrUpdatePattern(ctx, input)
	return &mcp.CallToolResult{}, output, err
}

func mcpImportAudioFiles(ctx context.Context, req *mcp.CallToolRequest, input tools.ImportAudioFilesInput) (*mcp.CallToolResult, tools.ImportAudioFilesOutput, error) {
	output, err := tools.ImportAudioFiles(ctx, input)
	return &mcp.CallToolResult{}, output, err
}

func mcpImportMLSelections(ctx context.Context, req *mcp.CallToolRequest, input tools.ImportMLSelectionsInput) (*mcp.CallToolResult, tools.ImportMLSelectionsOutput, error) {
	output, err := tools.ImportMLSelections(ctx, input)
	return &mcp.CallToolResult{}, output, err
}