package websocketspec

import (
	"context"
	"fmt"

	"github.com/bitechdev/ResolveSpec/pkg/common"
)

// HookType represents the type of lifecycle hook
type HookType string

const (
	// BeforeHandle fires after model resolution, before operation dispatch.
	// Use this for auth checks that need model rules and user context simultaneously.
	BeforeHandle HookType = "before_handle"

	// BeforeRead is called before a read operation
	BeforeRead HookType = "before_read"
	// AfterRead is called after a read operation
	AfterRead HookType = "after_read"

	// BeforeCreate is called before a create operation
	BeforeCreate HookType = "before_create"
	// AfterCreate is called after a create operation
	AfterCreate HookType = "after_create"

	// BeforeUpdate is called before an update operation
	BeforeUpdate HookType = "before_update"
	// AfterUpdate is called after an update operation
	AfterUpdate HookType = "after_update"

	// BeforeDelete is called before a delete operation
	BeforeDelete HookType = "before_delete"
	// AfterDelete is called after a delete operation
	AfterDelete HookType = "after_delete"

	// BeforeScan is called right before a read query is executed against the database,
	// after all filters/sort/pagination have been applied. Use this for row-level
	// security that needs to modify the query (stored in HookContext.Metadata["query"]).
	BeforeScan HookType = "before_scan"

	// BeforeSubscribe is called before creating a subscription
	BeforeSubscribe HookType = "before_subscribe"
	// AfterSubscribe is called after creating a subscription
	AfterSubscribe HookType = "after_subscribe"

	// BeforeUnsubscribe is called before removing a subscription
	BeforeUnsubscribe HookType = "before_unsubscribe"
	// AfterUnsubscribe is called after removing a subscription
	AfterUnsubscribe HookType = "after_unsubscribe"

	// BeforeConnect is called when a new connection is established
	BeforeConnect HookType = "before_connect"
	// AfterConnect is called after a connection is established
	AfterConnect HookType = "after_connect"

	// BeforeDisconnect is called before a connection is closed
	BeforeDisconnect HookType = "before_disconnect"
	// AfterDisconnect is called after a connection is closed
	AfterDisconnect HookType = "after_disconnect"

	// BeforeOp fires immediately before every SQL operation (read, create, update, delete).
	// Unlike BeforeHandle, which fires once before operation dispatch, BeforeOp fires at each
	// individual SQL-operation hook point, so it runs once per statement executed.
	BeforeOp HookType = "before_op"
)

// HookContext contains context information for hook execution
type HookContext struct {
	// Context is the request context
	Context context.Context

	// Handler provides access to the handler, database, and registry
	Handler *Handler

	// Connection is the WebSocket connection
	Connection *Connection

	// Message is the original message
	Message *Message

	// Schema is the database schema
	Schema string

	// Entity is the table/model name
	Entity string

	// TableName is the actual database table name
	TableName string

	// Model is the registered model instance
	Model interface{}

	// ModelPtr is a pointer to the model for queries
	ModelPtr interface{}

	// Options contains the parsed request options
	Options *common.RequestOptions

	// Operation being dispatched (e.g. "read", "create", "update", "delete")
	Operation string

	// ID is the record ID for single-record operations
	ID string

	// Data is the request data (for create/update operations)
	Data interface{}

	// Result is the operation result (for after hooks)
	Result interface{}

	// Subscription is the subscription being created/removed
	Subscription *Subscription

	// Error is any error that occurred (for after hooks)
	Error error

	// Allow hooks to abort the operation
	Abort        bool   // If set to true, the operation will be aborted
	AbortMessage string // Message to return if aborted
	AbortCode    int    // HTTP status code if aborted

	// Tx provides access to the database/transaction for executing additional SQL
	Tx common.Database

	// Metadata is additional context data
	Metadata map[string]interface{}
}

// HookFunc is a function that processes a hook
type HookFunc func(*HookContext) error

// HookRegistry manages lifecycle hooks
type HookRegistry struct {
	hooks map[HookType][]HookFunc
}

// NewHookRegistry creates a new hook registry
func NewHookRegistry() *HookRegistry {
	return &HookRegistry{
		hooks: make(map[HookType][]HookFunc),
	}
}

// Register registers a hook function for a specific hook type
func (hr *HookRegistry) Register(hookType HookType, fn HookFunc) {
	hr.hooks[hookType] = append(hr.hooks[hookType], fn)
}

// RegisterBefore registers a hook that runs before an operation
// Convenience method for BeforeRead, BeforeCreate, BeforeUpdate, BeforeDelete
func (hr *HookRegistry) RegisterBefore(operation OperationType, fn HookFunc) {
	switch operation {
	case OperationRead:
		hr.Register(BeforeRead, fn)
	case OperationCreate:
		hr.Register(BeforeCreate, fn)
	case OperationUpdate:
		hr.Register(BeforeUpdate, fn)
	case OperationDelete:
		hr.Register(BeforeDelete, fn)
	case OperationSubscribe:
		hr.Register(BeforeSubscribe, fn)
	case OperationUnsubscribe:
		hr.Register(BeforeUnsubscribe, fn)
	}
}

// RegisterAfter registers a hook that runs after an operation
// Convenience method for AfterRead, AfterCreate, AfterUpdate, AfterDelete
func (hr *HookRegistry) RegisterAfter(operation OperationType, fn HookFunc) {
	switch operation {
	case OperationRead:
		hr.Register(AfterRead, fn)
	case OperationCreate:
		hr.Register(AfterCreate, fn)
	case OperationUpdate:
		hr.Register(AfterUpdate, fn)
	case OperationDelete:
		hr.Register(AfterDelete, fn)
	case OperationSubscribe:
		hr.Register(AfterSubscribe, fn)
	case OperationUnsubscribe:
		hr.Register(AfterUnsubscribe, fn)
	}
}

// Execute runs all hooks for a specific type
func (hr *HookRegistry) Execute(hookType HookType, ctx *HookContext) error {
	hooks, exists := hr.hooks[hookType]
	if !exists {
		return nil
	}

	for _, hook := range hooks {
		if err := hook(ctx); err != nil {
			return err
		}

		// Check if hook requested abort
		if ctx.Abort {
			return fmt.Errorf("operation aborted by hook: %s", ctx.AbortMessage)
		}
	}

	return nil
}

// ExecuteBeforeOp executes the BeforeOp hook followed by the given SQL-operation hook
// (BeforeRead, BeforeCreate, BeforeUpdate, or BeforeDelete). BeforeOp always runs first
// so it can observe/veto every SQL operation regardless of type.
func (hr *HookRegistry) ExecuteBeforeOp(hookType HookType, ctx *HookContext) error {
	if err := hr.Execute(BeforeOp, ctx); err != nil {
		return err
	}
	return hr.Execute(hookType, ctx)
}

// HasHooks checks if any hooks are registered for a hook type
func (hr *HookRegistry) HasHooks(hookType HookType) bool {
	hooks, exists := hr.hooks[hookType]
	return exists && len(hooks) > 0
}

// Clear removes all hooks of a specific type
func (hr *HookRegistry) Clear(hookType HookType) {
	delete(hr.hooks, hookType)
}

// ClearAll removes all registered hooks
func (hr *HookRegistry) ClearAll() {
	hr.hooks = make(map[HookType][]HookFunc)
}
