package whatshooked

import (
	"context"
	"crypto/tls"
	"fmt"
	"net/http"
	"time"

	"git.warky.dev/wdevs/whatshooked/pkg/config"
	"git.warky.dev/wdevs/whatshooked/pkg/events"
	"git.warky.dev/wdevs/whatshooked/pkg/hooks"
	"git.warky.dev/wdevs/whatshooked/pkg/logging"
	"git.warky.dev/wdevs/whatshooked/pkg/utils"
	"go.mau.fi/whatsmeow/types"
	"golang.org/x/crypto/acme/autocert"
)

// Server is the optional built-in HTTP server
type Server struct {
	wh         *WhatsHooked
	httpServer *http.Server
}

// NewServer creates a new HTTP server instance
func NewServer(wh *WhatsHooked) *Server {
	return &Server{
		wh: wh,
	}
}

// Start starts the HTTP/HTTPS server
func (s *Server) Start() error {
	// Subscribe to hook success events for two-way communication
	s.wh.EventBus().Subscribe(events.EventHookSuccess, s.handleHookResponse)

	// Setup routes
	mux := s.setupRoutes()

	addr := fmt.Sprintf("%s:%d", s.wh.config.Server.Host, s.wh.config.Server.Port)
	s.httpServer = &http.Server{
		Addr:    addr,
		Handler: mux,
	}

	// Connect to WhatsApp accounts after server starts
	go func() {
		time.Sleep(100 * time.Millisecond) // Give server a moment to start
		logging.Info("Server ready, connecting to WhatsApp accounts")
		if err := s.wh.ConnectAll(context.Background()); err != nil {
			logging.Error("Failed to connect to WhatsApp accounts", "error", err)
		}
	}()

	// Start server with or without TLS
	if s.wh.config.Server.TLS.Enabled {
		return s.startTLS()
	}

	logging.Info("Starting HTTP server",
		"host", s.wh.config.Server.Host,
		"port", s.wh.config.Server.Port,
		"address", addr)

	// Start server (blocking)
	if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		return err
	}

	return nil
}

// startTLS starts the server with TLS based on the configured mode
func (s *Server) startTLS() error {
	tlsConfig := &s.wh.config.Server.TLS
	addr := fmt.Sprintf("%s:%d", s.wh.config.Server.Host, s.wh.config.Server.Port)

	switch tlsConfig.Mode {
	case "self-signed":
		return s.startSelfSignedTLS(tlsConfig, addr)
	case "custom":
		return s.startCustomTLS(tlsConfig, addr)
	case "autocert":
		return s.startAutocertTLS(tlsConfig, addr)
	default:
		return fmt.Errorf("invalid TLS mode: %s (must be 'self-signed', 'custom', or 'autocert')", tlsConfig.Mode)
	}
}

// startSelfSignedTLS starts the server with a self-signed certificate
func (s *Server) startSelfSignedTLS(tlsConfig *config.TLSConfig, addr string) error {
	logging.Info("Generating/loading self-signed certificate",
		"cert_dir", tlsConfig.CertDir,
		"host", s.wh.config.Server.Host)

	certPath, keyPath, err := utils.GenerateSelfSignedCert(tlsConfig.CertDir, s.wh.config.Server.Host)
	if err != nil {
		return fmt.Errorf("failed to generate self-signed certificate: %w", err)
	}

	logging.Info("Starting HTTPS server with self-signed certificate",
		"host", s.wh.config.Server.Host,
		"port", s.wh.config.Server.Port,
		"address", addr,
		"cert", certPath,
		"key", keyPath)

	if err := s.httpServer.ListenAndServeTLS(certPath, keyPath); err != nil && err != http.ErrServerClosed {
		return err
	}

	return nil
}

// startCustomTLS starts the server with custom certificate files
func (s *Server) startCustomTLS(tlsConfig *config.TLSConfig, addr string) error {
	if tlsConfig.CertFile == "" || tlsConfig.KeyFile == "" {
		return fmt.Errorf("custom TLS mode requires cert_file and key_file to be specified")
	}

	logging.Info("Validating custom TLS certificates",
		"cert", tlsConfig.CertFile,
		"key", tlsConfig.KeyFile)

	// Validate certificate files
	if err := utils.ValidateCertificateFiles(tlsConfig.CertFile, tlsConfig.KeyFile); err != nil {
		return fmt.Errorf("invalid certificate files: %w", err)
	}

	logging.Info("Starting HTTPS server with custom certificate",
		"host", s.wh.config.Server.Host,
		"port", s.wh.config.Server.Port,
		"address", addr,
		"cert", tlsConfig.CertFile,
		"key", tlsConfig.KeyFile)

	if err := s.httpServer.ListenAndServeTLS(tlsConfig.CertFile, tlsConfig.KeyFile); err != nil && err != http.ErrServerClosed {
		return err
	}

	return nil
}

// startAutocertTLS starts the server with Let's Encrypt autocert
func (s *Server) startAutocertTLS(tlsConfig *config.TLSConfig, addr string) error {
	if tlsConfig.Domain == "" {
		return fmt.Errorf("autocert mode requires domain to be specified")
	}

	logging.Info("Setting up Let's Encrypt autocert",
		"domain", tlsConfig.Domain,
		"email", tlsConfig.Email,
		"cache_dir", tlsConfig.CacheDir,
		"production", tlsConfig.Production)

	// Create autocert manager
	certManager := &autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(tlsConfig.Domain),
		Cache:      autocert.DirCache(tlsConfig.CacheDir),
		Email:      tlsConfig.Email,
	}

	// Configure TLS
	s.httpServer.TLSConfig = &tls.Config{
		GetCertificate: certManager.GetCertificate,
		MinVersion:     tls.VersionTLS12,
	}

	// Start HTTP-01 challenge server on port 80 if we're listening on 443
	if s.wh.config.Server.Port == 443 {
		go func() {
			httpAddr := fmt.Sprintf("%s:80", s.wh.config.Server.Host)
			logging.Info("Starting HTTP server for ACME challenges", "address", httpAddr)
			if err := http.ListenAndServe(httpAddr, certManager.HTTPHandler(nil)); err != nil {
				logging.Error("Failed to start HTTP challenge server", "error", err)
			}
		}()
	}

	logging.Info("Starting HTTPS server with Let's Encrypt",
		"host", s.wh.config.Server.Host,
		"port", s.wh.config.Server.Port,
		"address", addr,
		"domain", tlsConfig.Domain)

	if err := s.httpServer.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
		return err
	}

	return nil
}

// Stop stops the HTTP server gracefully
func (s *Server) Stop(ctx context.Context) error {
	if s.httpServer != nil {
		return s.httpServer.Shutdown(ctx)
	}
	return nil
}

// setupRoutes configures all HTTP routes
func (s *Server) setupRoutes() *http.ServeMux {
	mux := http.NewServeMux()
	h := s.wh.Handlers()

	// Landing page (no auth required)
	mux.HandleFunc("/", h.ServeIndex)

	// Privacy policy and terms of service (no auth required)
	mux.HandleFunc("/privacy-policy", h.ServePrivacyPolicy)
	mux.HandleFunc("/terms-of-service", h.ServeTermsOfService)

	// Static files (no auth required)
	mux.HandleFunc("/static/", h.ServeStatic)

	// Health check (no auth required)
	mux.HandleFunc("/health", h.Health)

	// Hook management (with auth)
	mux.HandleFunc("/api/hooks", h.Auth(h.Hooks))
	mux.HandleFunc("/api/hooks/add", h.Auth(h.AddHook))
	mux.HandleFunc("/api/hooks/remove", h.Auth(h.RemoveHook))

	// Account management (with auth)
	mux.HandleFunc("/api/accounts", h.Auth(h.Accounts))
	mux.HandleFunc("/api/accounts/status", h.Auth(h.AccountStatuses))
	mux.HandleFunc("/api/accounts/add", h.Auth(h.AddAccount))
	mux.HandleFunc("/api/accounts/update", h.Auth(h.UpdateAccount))
	mux.HandleFunc("/api/accounts/remove", h.Auth(h.RemoveAccount))
	mux.HandleFunc("/api/accounts/disable", h.Auth(h.DisableAccount))
	mux.HandleFunc("/api/accounts/enable", h.Auth(h.EnableAccount))

	// Send messages (with auth)
	mux.HandleFunc("/api/send", h.Auth(h.SendMessage))
	mux.HandleFunc("/api/send/image", h.Auth(h.SendImage))
	mux.HandleFunc("/api/send/video", h.Auth(h.SendVideo))
	mux.HandleFunc("/api/send/document", h.Auth(h.SendDocument))

	// Serve media files (with auth)
	mux.HandleFunc("/api/media/", h.ServeMedia)

	// Serve QR codes (no auth - needed during pairing)
	mux.HandleFunc("/api/qr/", h.ServeQRCode)

	// Business API webhooks (no auth - Meta validates via verify_token)
	mux.HandleFunc("/webhooks/whatsapp/", h.BusinessAPIWebhook)

	// Extended sending (with auth)
	mux.HandleFunc("/api/send/audio", h.Auth(h.SendAudio))
	mux.HandleFunc("/api/send/sticker", h.Auth(h.SendSticker))
	mux.HandleFunc("/api/send/location", h.Auth(h.SendLocation))
	mux.HandleFunc("/api/send/contacts", h.Auth(h.SendContacts))
	mux.HandleFunc("/api/send/interactive", h.Auth(h.SendInteractive))
	mux.HandleFunc("/api/send/template", h.Auth(h.SendTemplate))
	mux.HandleFunc("/api/send/flow", h.Auth(h.SendFlow))
	mux.HandleFunc("/api/send/reaction", h.Auth(h.SendReaction))
	mux.HandleFunc("/api/messages/read", h.Auth(h.MarkAsRead))

	// Template management (with auth)
	mux.HandleFunc("/api/templates", h.Auth(h.ListTemplates))
	mux.HandleFunc("/api/templates/upload", h.Auth(h.UploadTemplate))
	mux.HandleFunc("/api/templates/delete", h.Auth(h.DeleteTemplate))

	// Flow management (with auth)
	mux.HandleFunc("/api/flows", h.Auth(h.ListFlows))
	mux.HandleFunc("/api/flows/create", h.Auth(h.CreateFlow))
	mux.HandleFunc("/api/flows/get", h.Auth(h.GetFlow))
	mux.HandleFunc("/api/flows/upload", h.Auth(h.UploadFlowAsset))
	mux.HandleFunc("/api/flows/publish", h.Auth(h.PublishFlow))
	mux.HandleFunc("/api/flows/deprecate", h.Auth(h.DeprecateFlow))
	mux.HandleFunc("/api/flows/delete", h.Auth(h.DeleteFlow))

	// Phone number management (with auth)
	mux.HandleFunc("/api/phone-numbers", h.Auth(h.ListPhoneNumbers))
	mux.HandleFunc("/api/phone-numbers/request-code", h.Auth(h.RequestVerificationCode))
	mux.HandleFunc("/api/phone-numbers/verify-code", h.Auth(h.VerifyCode))
	mux.HandleFunc("/api/phone-numbers/register", h.Auth(h.RegisterPhoneNumber))

	// Media management (with auth)
	mux.HandleFunc("/api/media/upload", h.Auth(h.UploadMedia))
	mux.HandleFunc("/api/media-delete", h.Auth(h.DeleteMediaFile))

	// Business profile (with auth)
	mux.HandleFunc("/api/business-profile", h.Auth(h.GetBusinessProfile))
	mux.HandleFunc("/api/business-profile/update", h.Auth(h.UpdateBusinessProfile))

	// Catalog / commerce (with auth)
	mux.HandleFunc("/api/catalogs", h.Auth(h.ListCatalogs))
	mux.HandleFunc("/api/catalogs/products", h.Auth(h.ListProducts))
	mux.HandleFunc("/api/send/catalog", h.Auth(h.SendCatalogMessage))
	mux.HandleFunc("/api/send/product", h.Auth(h.SendSingleProduct))
	mux.HandleFunc("/api/send/product-list", h.Auth(h.SendProductList))

	// Message cache management (with auth)
	mux.HandleFunc("/api/cache", h.Auth(h.GetCachedEvents))                // GET - list cached events
	mux.HandleFunc("/api/cache/stats", h.Auth(h.GetCacheStats))            // GET - cache statistics
	mux.HandleFunc("/api/cache/replay", h.Auth(h.ReplayCachedEvents))      // POST - replay all
	mux.HandleFunc("/api/cache/event", h.Auth(h.GetCachedEvent))           // GET with ?id=
	mux.HandleFunc("/api/cache/event/replay", h.Auth(h.ReplayCachedEvent)) // POST with ?id=
	mux.HandleFunc("/api/cache/event/delete", h.Auth(h.DeleteCachedEvent)) // DELETE with ?id=
	mux.HandleFunc("/api/cache/clear", h.Auth(h.ClearCache))               // DELETE with ?confirm=true

	logging.Info("HTTP server endpoints configured",
		"index", "/",
		"static", "/static/*",
		"health", "/health",
		"hooks", "/api/hooks",
		"accounts", "/api/accounts",
		"send", "/api/send",
		"cache", "/api/cache",
		"webhooks", "/webhooks/whatsapp/*",
		"qr", "/api/qr")

	return mux
}

// handleHookResponse processes hook success events for two-way communication
func (s *Server) 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(s.wh.config.WhatsApp) > 0 {
		targetAccountID = s.wh.config.WhatsApp[0].ID
	}

	// Format phone number to JID format
	formattedJID := utils.FormatPhoneToJID(resp.To, s.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 := s.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)
	}
}
