// Package websocketspec provides a WebSocket-based API specification for real-time
// CRUD operations with bidirectional communication and subscription support.
//
// # Key Features
//
//   - Real-time bidirectional communication over WebSocket
//   - CRUD operations (Create, Read, Update, Delete)
//   - Real-time subscriptions with filtering
//   - Lifecycle hooks for all operations
//   - Database-agnostic: Works with GORM and Bun ORM through adapters
//   - Automatic change notifications to subscribers
//   - Connection and subscription management
//
// # Message Protocol
//
// WebSocketSpec uses JSON messages for communication:
//
//	{
//	  "id": "unique-message-id",
//	  "type": "request|response|notification|subscription",
//	  "operation": "read|create|update|delete|subscribe|unsubscribe",
//	  "schema": "public",
//	  "entity": "users",
//	  "data": {...},
//	  "options": {
//	    "filters": [...],
//	    "columns": [...],
//	    "preload": [...],
//	    "sort": [...],
//	    "limit": 10
//	  }
//	}
//
// # Usage Example
//
//	// Create handler with GORM
//	handler := websocketspec.NewHandlerWithGORM(db)
//
//	// Register models
//	handler.Registry.RegisterModel("public.users", &User{})
//
//	// Setup WebSocket endpoint
//	http.HandleFunc("/ws", handler.HandleWebSocket)
//
//	// Start server
//	http.ListenAndServe(":8080", nil)
//
// # Client Example
//
//	// Connect to WebSocket
//	ws := new WebSocket("ws://localhost:8080/ws")
//
//	// Send read request
//	ws.send(JSON.stringify({
//	  id: "msg-1",
//	  type: "request",
//	  operation: "read",
//	  entity: "users",
//	  options: {
//	    filters: [{column: "status", operator: "eq", value: "active"}],
//	    limit: 10
//	  }
//	}))
//
//	// Subscribe to changes
//	ws.send(JSON.stringify({
//	  id: "msg-2",
//	  type: "subscription",
//	  operation: "subscribe",
//	  entity: "users",
//	  options: {
//	    filters: [{column: "status", operator: "eq", value: "active"}]
//	  }
//	}))
package websocketspec

import (
	"github.com/uptrace/bun"
	"gorm.io/gorm"

	"github.com/bitechdev/ResolveSpec/pkg/common"
	"github.com/bitechdev/ResolveSpec/pkg/common/adapters/database"
	"github.com/bitechdev/ResolveSpec/pkg/modelregistry"
)

// NewHandlerWithGORM creates a new Handler with GORM adapter
func NewHandlerWithGORM(db *gorm.DB) *Handler {
	gormAdapter := database.NewGormAdapter(db)
	registry := modelregistry.NewModelRegistry()
	return NewHandler(gormAdapter, registry)
}

// NewHandlerWithBun creates a new Handler with Bun adapter
func NewHandlerWithBun(db *bun.DB) *Handler {
	bunAdapter := database.NewBunAdapter(db)
	registry := modelregistry.NewModelRegistry()
	return NewHandler(bunAdapter, registry)
}

// NewHandlerWithDatabase creates a new Handler with a custom database adapter
func NewHandlerWithDatabase(db common.Database, registry common.ModelRegistry) *Handler {
	return NewHandler(db, registry)
}

// Example usage functions for documentation:

// ExampleWithGORM shows how to use WebSocketSpec with GORM
func ExampleWithGORM(db *gorm.DB) {
	// Create handler using GORM
	handler := NewHandlerWithGORM(db)

	// Register models
	_ = handler.Registry().RegisterModel("public.users", &struct{}{})

	// Register hooks (optional)
	handler.Hooks().RegisterBefore(OperationRead, func(ctx *HookContext) error {
		// Add custom logic before read operations
		return nil
	})

	// Setup WebSocket endpoint
	// http.HandleFunc("/ws", handler.HandleWebSocket)

	// Start server
	// http.ListenAndServe(":8080", nil)
}

// ExampleWithBun shows how to use WebSocketSpec with Bun ORM
func ExampleWithBun(bunDB *bun.DB) {
	// Create handler using Bun
	handler := NewHandlerWithBun(bunDB)

	// Register models
	_ = handler.Registry().RegisterModel("public.users", &struct{}{})

	// Setup WebSocket endpoint
	// http.HandleFunc("/ws", handler.HandleWebSocket)
}

// ExampleWithHooks shows how to use lifecycle hooks
func ExampleWithHooks(db *gorm.DB) {
	handler := NewHandlerWithGORM(db)

	// Register a before-read hook for authorization
	handler.Hooks().RegisterBefore(OperationRead, func(ctx *HookContext) error {
		// Check if user has permission to read this entity
		// return fmt.Errorf("unauthorized") if not allowed
		return nil
	})

	// Register an after-create hook for logging
	handler.Hooks().RegisterAfter(OperationCreate, func(ctx *HookContext) error {
		// Log the created record
		// logger.Info("Created record: %v", ctx.Result)
		return nil
	})

	// Register a before-subscribe hook to limit subscriptions
	handler.Hooks().Register(BeforeSubscribe, func(ctx *HookContext) error {
		// Limit number of subscriptions per connection
		// if len(ctx.Connection.subscriptions) >= 10 {
		//     return fmt.Errorf("maximum subscriptions reached")
		// }
		return nil
	})
}

// ExampleWithSubscriptions shows subscription usage
func ExampleWithSubscriptions() {
	// Client-side JavaScript example:
	/*
		const ws = new WebSocket("ws://localhost:8080/ws");

		// Subscribe to user changes
		ws.send(JSON.stringify({
			id: "sub-1",
			type: "subscription",
			operation: "subscribe",
			schema: "public",
			entity: "users",
			options: {
				filters: [
					{column: "status", operator: "eq", value: "active"}
				]
			}
		}));

		// Handle notifications
		ws.onmessage = (event) => {
			const msg = JSON.parse(event.data);
			if (msg.type === "notification") {
				console.log("User changed:", msg.data);
				console.log("Operation:", msg.operation); // create, update, or delete
			}
		};

		// Unsubscribe
		ws.send(JSON.stringify({
			id: "unsub-1",
			type: "subscription",
			operation: "unsubscribe",
			subscription_id: "sub-abc123"
		}));
	*/
}

// ExampleCRUDOperations shows basic CRUD operations
func ExampleCRUDOperations() {
	// Client-side JavaScript example:
	/*
		const ws = new WebSocket("ws://localhost:8080/ws");

		// CREATE - Create a new user
		ws.send(JSON.stringify({
			id: "create-1",
			type: "request",
			operation: "create",
			schema: "public",
			entity: "users",
			data: {
				name: "John Doe",
				email: "john@example.com",
				status: "active"
			}
		}));

		// READ - Get all active users
		ws.send(JSON.stringify({
			id: "read-1",
			type: "request",
			operation: "read",
			schema: "public",
			entity: "users",
			options: {
				filters: [{column: "status", operator: "eq", value: "active"}],
				columns: ["id", "name", "email"],
				sort: [{column: "name", direction: "asc"}],
				limit: 10
			}
		}));

		// READ BY ID - Get a specific user
		ws.send(JSON.stringify({
			id: "read-2",
			type: "request",
			operation: "read",
			schema: "public",
			entity: "users",
			record_id: "123"
		}));

		// UPDATE - Update a user
		ws.send(JSON.stringify({
			id: "update-1",
			type: "request",
			operation: "update",
			schema: "public",
			entity: "users",
			record_id: "123",
			data: {
				name: "John Updated",
				email: "john.updated@example.com"
			}
		}));

		// DELETE - Delete a user
		ws.send(JSON.stringify({
			id: "delete-1",
			type: "request",
			operation: "delete",
			schema: "public",
			entity: "users",
			record_id: "123"
		}));

		// Handle responses
		ws.onmessage = (event) => {
			const response = JSON.parse(event.data);
			if (response.type === "response") {
				if (response.success) {
					console.log("Operation successful:", response.data);
				} else {
					console.error("Operation failed:", response.error);
				}
			}
		};
	*/
}

// ExampleAuthentication shows how to implement authentication
func ExampleAuthentication() {
	// Server-side example with authentication hook:
	/*
		handler := NewHandlerWithGORM(db)

		// Register before-connect hook for authentication
		handler.Hooks().Register(BeforeConnect, func(ctx *HookContext) error {
			// Extract token from query params or headers
			r := ctx.Connection.ws.UnderlyingConn().RemoteAddr()

			// Validate token
			// token := extractToken(r)
			// user, err := validateToken(token)
			// if err != nil {
			//     return fmt.Errorf("authentication failed: %w", err)
			// }

			// Store user info in connection metadata
			// ctx.Connection.SetMetadata("user", user)
			// ctx.Connection.SetMetadata("user_id", user.ID)

			return nil
		})

		// Use connection metadata in other hooks
		handler.Hooks().RegisterBefore(OperationRead, func(ctx *HookContext) error {
			// Get user from connection metadata
			// userID, _ := ctx.Connection.GetMetadata("user_id")

			// Add filter to only show user's own records
			// if ctx.Entity == "orders" {
			//     ctx.Options.Filters = append(ctx.Options.Filters, common.FilterOption{
			//         Column:   "user_id",
			//         Operator: "eq",
			//         Value:    userID,
			//     })
			// }

			return nil
		})
	*/
}
