package resources

import (
	"context"
	"fmt"
	"os"
	"strings"

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

var schemaPath string

// Table names available in the database
var tableNames = []string{
	"dataset",
	"location",
	"cyclic_recording_pattern",
	"cluster",
	"file",
	"moth_metadata",
	"file_metadata",
	"file_dataset",
	"selection",
	"selection_metadata",
	"ebird_taxonomy",
	"species",
	"call_type",
	"filter",
	"label",
	"label_subtype",
	"ebird_taxonomy_v2024",
	"species_dataset",
}

// SetSchemaPath sets the path to the schema.sql file
func SetSchemaPath(path string) {
	schemaPath = path
}

// GetSchemaResources returns the resource definitions for registration
func GetSchemaResources() (*mcp.Resource, *mcp.ResourceTemplate) {
	// Direct resource for full schema
	fullSchemaResource := &mcp.Resource{
		URI:         "schema://full",
		Name:        "Database Schema",
		Description: "Complete SQL schema for the skraak database including all tables, indexes, and types",
		MIMEType:    "application/sql",
	}

	// Template resource for individual tables
	tableTemplate := &mcp.ResourceTemplate{
		URITemplate: "schema://table/{table_name}",
		Name:        "Table Schema",
		Description: "SQL schema for a specific table. Available tables: dataset, location, cyclic_recording_pattern, cluster, file, moth_metadata, file_metadata, file_dataset, selection, selection_metadata, ebird_taxonomy, species, call_type, filter, label, label_subtype, ebird_taxonomy_v2024, species_dataset",
		MIMEType:    "application/sql",
	}

	return fullSchemaResource, tableTemplate
}

// SchemaResourceHandler handles resource read requests for schema
func SchemaResourceHandler(ctx context.Context, req *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) {
	uri := req.Params.URI

	// Handle full schema request
	if uri == "schema://full" {
		return readFullSchema()
	}

	// Handle table-specific request
	if strings.HasPrefix(uri, "schema://table/") {
		tableName := strings.TrimPrefix(uri, "schema://table/")
		return readTableSchema(tableName)
	}

	return nil, fmt.Errorf("unknown resource URI: %s", uri)
}

// readFullSchema reads and returns the complete schema file
func readFullSchema() (*mcp.ReadResourceResult, error) {
	if schemaPath == "" {
		return nil, fmt.Errorf("schema path not set")
	}

	content, err := os.ReadFile(schemaPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read schema file: %w", err)
	}

	return &mcp.ReadResourceResult{
		Contents: []*mcp.ResourceContents{
			{
				URI:      "schema://full",
				MIMEType: "application/sql",
				Text:     string(content),
			},
		},
	}, nil
}

// readTableSchema extracts and returns the schema for a specific table
func readTableSchema(tableName string) (*mcp.ReadResourceResult, error) {
	if schemaPath == "" {
		return nil, fmt.Errorf("schema path not set")
	}

	// Validate table name
	if !isValidTableName(tableName) {
		return nil, fmt.Errorf("invalid table name: %s. Valid tables: %s", tableName, strings.Join(tableNames, ", "))
	}

	content, err := os.ReadFile(schemaPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read schema file: %w", err)
	}

	// Extract table definition
	tableDef, err := extractTableDefinition(string(content), tableName)
	if err != nil {
		return nil, err
	}

	return &mcp.ReadResourceResult{
		Contents: []*mcp.ResourceContents{
			{
				URI:      fmt.Sprintf("schema://table/%s", tableName),
				MIMEType: "application/sql",
				Text:     tableDef,
			},
		},
	}, nil
}

// isValidTableName checks if the table name is in the list of valid tables
func isValidTableName(name string) bool {
	for _, validName := range tableNames {
		if name == validName {
			return true
		}
	}
	return false
}

// extractTableDefinition extracts the CREATE TABLE statement for a specific table
// Uses simple line-based parsing to find the table definition
func extractTableDefinition(schema string, tableName string) (string, error) {
	lines := strings.Split(schema, "\n")
	var tableLines []string
	inTable := false
	parenCount := 0

	// Special handling for views (CREATE TABLE ... AS)
	isView := false

	for _, line := range lines {
		// Look for CREATE TABLE or CREATE TYPE statements for this table/type
		if strings.Contains(line, "CREATE TABLE "+tableName) ||
			strings.Contains(line, "CREATE TABLE "+tableName+" AS") ||
			strings.Contains(line, "CREATE TYPE "+tableName) {
			inTable = true
			tableLines = append(tableLines, line)

			// Check if it's a view (CREATE TABLE ... AS)
			if strings.Contains(line, " AS") {
				isView = true
			}

			// Count parentheses
			parenCount += strings.Count(line, "(") - strings.Count(line, ")")

			// For views with AS SELECT, check if statement ends with semicolon
			if isView && strings.HasSuffix(strings.TrimSpace(line), ";") {
				break
			}

			continue
		}

		if inTable {
			tableLines = append(tableLines, line)
			parenCount += strings.Count(line, "(") - strings.Count(line, ")")

			// End of table definition
			if isView {
				// For views, look for semicolon
				if strings.HasSuffix(strings.TrimSpace(line), ";") {
					break
				}
			} else {
				// For regular tables, check for closing parenthesis and semicolon
				if parenCount == 0 && strings.Contains(line, ");") {
					break
				}
			}
		}
	}

	if len(tableLines) == 0 {
		return "", fmt.Errorf("table definition not found: %s", tableName)
	}

	// Also include related indexes and constraints
	tableLines = append(tableLines, "")
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if strings.Contains(trimmed, "CREATE INDEX") && strings.Contains(trimmed, " "+tableName+"(") {
			tableLines = append(tableLines, line)
		}
		if strings.Contains(trimmed, "ALTER TABLE "+tableName) {
			tableLines = append(tableLines, line)
		}
	}

	return strings.Join(tableLines, "\n"), nil
}