package tools

import (
	"context"
	"fmt"
	"strings"
	"time"

	"skraak_mcp/db"

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

// CreateLocationInput defines the input parameters for the create_location tool
type CreateLocationInput struct {
	DatasetID   string  `json:"dataset_id" jsonschema:"required,ID of the parent dataset (12-character nanoid)"`
	Name        string  `json:"name" jsonschema:"required,Location name (max 140 characters)"`
	Latitude    float64 `json:"latitude" jsonschema:"required,Latitude in decimal degrees (-90 to 90)"`
	Longitude   float64 `json:"longitude" jsonschema:"required,Longitude in decimal degrees (-180 to 180)"`
	TimezoneID  string  `json:"timezone_id" jsonschema:"required,IANA timezone ID (e.g. 'Pacific/Auckland')"`
	Description *string `json:"description,omitempty" jsonschema:"Optional location description (max 255 characters)"`
}

// CreateLocationOutput defines the output structure
type CreateLocationOutput struct {
	Location db.Location `json:"location" jsonschema:"The created location with generated ID and timestamps"`
	Message  string      `json:"message" jsonschema:"Success message"`
}

// CreateLocation implements the create_location tool handler
// Creates a new location within a dataset with GPS coordinates and timezone
func CreateLocation(
	ctx context.Context,
	req *mcp.CallToolRequest,
	input CreateLocationInput,
) (*mcp.CallToolResult, CreateLocationOutput, error) {
	var output CreateLocationOutput

	// Validate name
	if strings.TrimSpace(input.Name) == "" {
		return nil, output, fmt.Errorf("name cannot be empty")
	}
	if len(input.Name) > 140 {
		return nil, output, fmt.Errorf("name must be 140 characters or less (got %d)", len(input.Name))
	}

	// Validate description length if provided
	if input.Description != nil && len(*input.Description) > 255 {
		return nil, output, fmt.Errorf("description must be 255 characters or less (got %d)", len(*input.Description))
	}

	// Validate coordinates
	if input.Latitude < -90 || input.Latitude > 90 {
		return nil, output, fmt.Errorf("latitude must be between -90 and 90 (got %f)", input.Latitude)
	}
	if input.Longitude < -180 || input.Longitude > 180 {
		return nil, output, fmt.Errorf("longitude must be between -180 and 180 (got %f)", input.Longitude)
	}

	// Validate timezone
	if _, err := time.LoadLocation(input.TimezoneID); err != nil {
		return nil, output, fmt.Errorf("invalid timezone_id '%s': %w", input.TimezoneID, err)
	}

	// Validate dataset_id not empty
	if strings.TrimSpace(input.DatasetID) == "" {
		return nil, output, fmt.Errorf("dataset_id cannot be empty")
	}

	// Open writable database connection
	database, err := db.OpenWriteableDB(dbPath)
	if err != nil {
		return nil, output, fmt.Errorf("database connection failed: %w", err)
	}
	defer database.Close()

	// Begin transaction
	tx, err := database.BeginTx(ctx, nil)
	if err != nil {
		return nil, output, fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer func() {
		if err != nil {
			tx.Rollback()
		}
	}()

	// Verify dataset exists and is active
	var datasetExists bool
	var datasetActive bool
	var datasetName string
	err = tx.QueryRowContext(ctx,
		"SELECT EXISTS(SELECT 1 FROM dataset WHERE id = ?), active, name FROM dataset WHERE id = ?",
		input.DatasetID, input.DatasetID,
	).Scan(&datasetExists, &datasetActive, &datasetName)
	if err != nil {
		return nil, output, fmt.Errorf("failed to verify dataset: %w", err)
	}
	if !datasetExists {
		return nil, output, fmt.Errorf("dataset with ID '%s' does not exist", input.DatasetID)
	}
	if !datasetActive {
		return nil, output, fmt.Errorf("dataset '%s' (ID: %s) is not active", datasetName, input.DatasetID)
	}

	// Generate ID
	id, err := db.GenerateID()
	if err != nil {
		return nil, output, fmt.Errorf("failed to generate ID: %w", err)
	}

	// Insert location (explicitly set timestamps and active for schema compatibility)
	_, err = tx.ExecContext(ctx,
		"INSERT INTO location (id, dataset_id, name, latitude, longitude, timezone_id, description, created_at, last_modified, active) VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, TRUE)",
		id, input.DatasetID, input.Name, input.Latitude, input.Longitude, input.TimezoneID, input.Description,
	)
	if err != nil {
		return nil, output, fmt.Errorf("failed to create location: %w", err)
	}

	// Fetch the created location (gets DB-generated timestamps and defaults)
	var location db.Location
	err = tx.QueryRowContext(ctx,
		"SELECT id, dataset_id, name, latitude, longitude, description, created_at, last_modified, active, timezone_id FROM location WHERE id = ?",
		id,
	).Scan(&location.ID, &location.DatasetID, &location.Name, &location.Latitude, &location.Longitude,
		&location.Description, &location.CreatedAt, &location.LastModified, &location.Active, &location.TimezoneID)
	if err != nil {
		return nil, output, fmt.Errorf("failed to fetch created location: %w", err)
	}

	// Commit transaction
	if err = tx.Commit(); err != nil {
		return nil, output, fmt.Errorf("failed to commit transaction: %w", err)
	}

	output.Location = location
	output.Message = fmt.Sprintf("Successfully created location '%s' with ID %s in dataset '%s' (%.6f, %.6f, %s)",
		location.Name, location.ID, datasetName, location.Latitude, location.Longitude, location.TimezoneID)

	return &mcp.CallToolResult{}, output, nil
}