package whatshooked

import (
	"context"
	"time"

	"git.warky.dev/wdevs/whatshooked/pkg/api"
	"git.warky.dev/wdevs/whatshooked/pkg/cache"
	"git.warky.dev/wdevs/whatshooked/pkg/config"
	"git.warky.dev/wdevs/whatshooked/pkg/eventlogger"
	"git.warky.dev/wdevs/whatshooked/pkg/events"
	"git.warky.dev/wdevs/whatshooked/pkg/handlers"
	"git.warky.dev/wdevs/whatshooked/pkg/hooks"
	"git.warky.dev/wdevs/whatshooked/pkg/logging"
	"git.warky.dev/wdevs/whatshooked/pkg/storage"
	"git.warky.dev/wdevs/whatshooked/pkg/utils"
	"git.warky.dev/wdevs/whatshooked/pkg/whatsapp"
	"go.mau.fi/whatsmeow/types"
)

// WhatsHooked is the main library instance
type WhatsHooked struct {
	config       *config.Config
	configPath   string
	eventBus     *events.EventBus
	whatsappMgr  *whatsapp.Manager
	hookMgr      *hooks.Manager
	eventLogger  *eventlogger.Logger
	messageCache *cache.MessageCache
	handlers     *handlers.Handlers
	apiServer    *api.Server // ResolveSpec unified server
}

// NewFromFile creates a WhatsHooked instance from a config file
func NewFromFile(configPath string) (*WhatsHooked, error) {
	cfg, err := config.Load(configPath)
	if err != nil {
		return nil, err
	}

	// Initialize logging from config
	logging.Init(cfg.LogLevel)

	return newWithConfig(cfg, configPath)
}

// New creates a WhatsHooked instance with programmatic config
func New(opts ...Option) (*WhatsHooked, error) {
	cfg := &config.Config{
		Server: config.ServerConfig{
			Host: "0.0.0.0",
			Port: 8825,
		},
		Media: config.MediaConfig{
			DataPath: "./data/media",
			Mode:     "link",
		},
		LogLevel: "info",
	}

	// Apply options
	for _, opt := range opts {
		opt(cfg)
	}

	// Initialize logging from config
	logging.Init(cfg.LogLevel)

	return newWithConfig(cfg, "")
}

// newWithConfig is the internal constructor
func newWithConfig(cfg *config.Config, configPath string) (*WhatsHooked, error) {
	wh := &WhatsHooked{
		config:     cfg,
		configPath: configPath,
		eventBus:   events.NewEventBus(),
	}

	// Initialize WhatsApp manager
	wh.whatsappMgr = whatsapp.NewManager(
		wh.eventBus,
		cfg.Media,
		cfg,
		configPath,
		func(updatedCfg *config.Config) error {
			if configPath != "" {
				return config.Save(configPath, updatedCfg)
			}
			return nil
		},
	)

	// Initialize message cache
	cacheConfig := cache.Config{
		Enabled:   cfg.MessageCache.Enabled,
		DataPath:  cfg.MessageCache.DataPath,
		MaxAge:    time.Duration(cfg.MessageCache.MaxAgeDays) * 24 * time.Hour,
		MaxEvents: cfg.MessageCache.MaxEvents,
	}

	messageCache, err := cache.NewMessageCache(cacheConfig)
	if err != nil {
		logging.Error("Failed to initialize message cache", "error", err)
		// Continue without cache rather than failing
		messageCache = &cache.MessageCache{}
	}
	wh.messageCache = messageCache

	// Initialize hook manager
	wh.hookMgr = hooks.NewManager(wh.eventBus, wh.messageCache)
	wh.hookMgr.LoadHooks(cfg.Hooks)
	wh.hookMgr.Start()

	// Initialize event logger if enabled
	if cfg.EventLogger.Enabled && len(cfg.EventLogger.Targets) > 0 {
		logger, err := eventlogger.NewLogger(cfg.EventLogger, cfg.Database, wh.whatsappMgr, cfg.Server.DefaultCountryCode)
		if err == nil {
			wh.eventLogger = logger
			wh.eventBus.SubscribeAll(func(event events.Event) {
				wh.eventLogger.Log(event)
			})
			logging.Info("Event logger initialized", "targets", cfg.EventLogger.Targets)
		} else {
			logging.Error("Failed to initialize event logger", "error", err)
		}
	}

	// Create handlers
	wh.handlers = handlers.New(wh.whatsappMgr, wh.hookMgr, cfg, configPath)

	return wh, nil
}

// ConnectAll connects to all configured WhatsApp accounts
func (wh *WhatsHooked) ConnectAll(ctx context.Context) error {
	for _, waCfg := range wh.config.WhatsApp {
		// Skip disabled accounts
		if waCfg.Disabled {
			logging.Info("Skipping disabled account", "account_id", waCfg.ID)
			continue
		}

		if err := wh.whatsappMgr.Connect(ctx, waCfg); err != nil {
			logging.Error("Failed to connect to WhatsApp", "account_id", waCfg.ID, "error", err)
			// Continue connecting to other accounts even if one fails
		}
	}
	return nil
}

// Handlers returns the HTTP handlers instance
func (wh *WhatsHooked) Handlers() *handlers.Handlers {
	return wh.handlers
}

// Manager returns the WhatsApp manager
func (wh *WhatsHooked) Manager() *whatsapp.Manager {
	return wh.whatsappMgr
}

// EventBus returns the event bus
func (wh *WhatsHooked) EventBus() *events.EventBus {
	return wh.eventBus
}

// HookManager returns the hook manager
func (wh *WhatsHooked) HookManager() *hooks.Manager {
	return wh.hookMgr
}

// Config returns the configuration
func (wh *WhatsHooked) Config() *config.Config {
	return wh.config
}

// Close shuts down all components gracefully
func (wh *WhatsHooked) Close() error {
	wh.whatsappMgr.DisconnectAll()
	if wh.eventLogger != nil {
		return wh.eventLogger.Close()
	}
	return nil
}

// StartServer starts the ResolveSpec HTTP server
func (wh *WhatsHooked) StartServer(ctx context.Context) error {
	return wh.StartAPIServer(ctx)
}

// StopServer stops the ResolveSpec HTTP server
func (wh *WhatsHooked) StopServer(ctx context.Context) error {
	return wh.StopAPIServer(ctx)
}

// StartAPIServer starts the unified ResolveSpec server
func (wh *WhatsHooked) StartAPIServer(ctx context.Context) error {
	// Subscribe to hook success events for two-way communication
	wh.eventBus.Subscribe(events.EventHookSuccess, wh.handleHookResponse)

	// Initialize database
	logging.Info("Initializing database")
	if err := storage.Initialize(&wh.config.Database); err != nil {
		return err
	}

	db := storage.GetDB()

	// Create tables
	logging.Info("Creating database tables")
	if err := storage.CreateTables(ctx); err != nil {
		return err
	}

	// Seed initial data (creates admin user if not exists)
	logging.Info("Seeding initial data")
	if err := storage.SeedData(ctx); err != nil {
		return err
	}

	// Create unified server
	logging.Info("Creating ResolveSpec server", "host", wh.config.Server.Host, "port", wh.config.Server.Port)
	apiServer, err := api.NewServer(wh.config, db, wh)
	if err != nil {
		return err
	}

	wh.apiServer = apiServer

	// Start the server in a goroutine (non-blocking)
	go func() {
		if err := wh.apiServer.Start(); err != nil {
			logging.Error("ResolveSpec server error", "error", err)
		}
	}()

	logging.Info("ResolveSpec server started successfully")
	return nil
}

// StopAPIServer stops the ResolveSpec server
func (wh *WhatsHooked) StopAPIServer(ctx context.Context) error {
	if wh.apiServer != nil {
		logging.Info("Stopping ResolveSpec server")
		// The server manager handles graceful shutdown internally
		// We just need to close the database
		return storage.Close()
	}
	return nil
}

// handleHookResponse processes hook success events for two-way communication
func (wh *WhatsHooked) handleHookResponse(event events.Event) {
	// Use event context for sending message
	ctx := event.Context
	if ctx == nil {
		ctx = context.Background()
	}

	// Extract response from event data
	responseData, ok := event.Data["response"]
	if !ok || responseData == nil {
		return
	}

	// Try to cast to HookResponse
	resp, ok := responseData.(hooks.HookResponse)
	if !ok {
		return
	}

	if !resp.SendMessage {
		return
	}

	// Determine which account to use - default to first available if not specified
	targetAccountID := resp.AccountID
	if targetAccountID == "" && len(wh.config.WhatsApp) > 0 {
		targetAccountID = wh.config.WhatsApp[0].ID
	}

	// Format phone number to JID format
	formattedJID := utils.FormatPhoneToJID(resp.To, wh.config.Server.DefaultCountryCode)

	// Parse JID
	jid, err := types.ParseJID(formattedJID)
	if err != nil {
		logging.Error("Invalid JID in hook response", "jid", formattedJID, "error", err)
		return
	}

	// Send message with context
	if err := wh.whatsappMgr.SendTextMessage(ctx, targetAccountID, jid, resp.Text); err != nil {
		logging.Error("Failed to send message from hook response", "error", err)
	} else {
		logging.Info("Message sent from hook response", "account_id", targetAccountID, "to", resp.To)
	}
}
