package proxy

import (
	"bytes"
	"context"
	"encoding/json"
	"net"
	"net/http"
	"sync"
	"time"

	"github.com/standardbeagle/agnt/internal/debug"
)

// OverlayNotifier sends events to the agent overlay server via Unix socket.
type OverlayNotifier struct {
	socketPath string
	client     *http.Client
	enabled    bool
	mu         sync.RWMutex
}

// OverlayEvent represents an event to send to the overlay.
type OverlayEvent struct {
	Type      string      `json:"type"`
	ProxyID   string      `json:"proxy_id"`
	Timestamp time.Time   `json:"timestamp"`
	Data      interface{} `json:"data"`
}

// NewOverlayNotifier creates a new overlay notifier.
func NewOverlayNotifier() *OverlayNotifier {
	return &OverlayNotifier{
		enabled: false,
	}
}

// SetEndpoint sets the overlay socket path.
// Example: "/run/user/1000/devtool-overlay.sock" or "\\.\pipe\devtool-overlay-user"
func (n *OverlayNotifier) SetEndpoint(socketPath string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.socketPath = socketPath
	n.enabled = socketPath != ""

	if n.enabled {
		debug.Log("overlay-notifier", "endpoint set: %s", socketPath)
		// Create HTTP client with Unix socket transport
		n.client = &http.Client{
			Timeout: 5 * time.Second,
			Transport: &http.Transport{
				DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
					return net.Dial("unix", socketPath)
				},
			},
		}
	} else {
		debug.Log("overlay-notifier", "endpoint cleared")
		n.client = nil
	}
}

// GetEndpoint returns the current socket path.
func (n *OverlayNotifier) GetEndpoint() string {
	n.mu.RLock()
	defer n.mu.RUnlock()
	return n.socketPath
}

// IsEnabled returns whether the notifier is enabled.
func (n *OverlayNotifier) IsEnabled() bool {
	n.mu.RLock()
	defer n.mu.RUnlock()
	return n.enabled
}

// NotifyPanelMessage sends a panel message to the overlay.
func (n *OverlayNotifier) NotifyPanelMessage(proxyID string, msg *PanelMessage) error {
	return n.send(OverlayEvent{
		Type:      "panel_message",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      msg,
	})
}

// NotifySketch sends a sketch to the overlay.
func (n *OverlayNotifier) NotifySketch(proxyID string, sketch *SketchEntry) error {
	return n.send(OverlayEvent{
		Type:      "sketch",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      sketch,
	})
}

// NotifyDesignState sends design state to the overlay.
func (n *OverlayNotifier) NotifyDesignState(proxyID string, state *DesignState) error {
	return n.send(OverlayEvent{
		Type:      "design_state",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      state,
	})
}

// NotifyDesignRequest sends a design request to the overlay.
func (n *OverlayNotifier) NotifyDesignRequest(proxyID string, request *DesignRequest) error {
	return n.send(OverlayEvent{
		Type:      "design_request",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      request,
	})
}

// NotifyDesignChat sends a design chat message to the overlay.
func (n *OverlayNotifier) NotifyDesignChat(proxyID string, chat *DesignChat) error {
	return n.send(OverlayEvent{
		Type:      "design_chat",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      chat,
	})
}

// NotifyDesignEdit sends a committed design geometry edit to the overlay.
func (n *OverlayNotifier) NotifyDesignEdit(proxyID string, edit *DesignEdit) error {
	return n.send(OverlayEvent{
		Type:      "design_edit",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      edit,
	})
}

// NotifyResponsiveRequest sends a responsive mode handoff request to the overlay.
func (n *OverlayNotifier) NotifyResponsiveRequest(proxyID string, request *ResponsiveRequest) error {
	return n.send(OverlayEvent{
		Type:      "responsive_request",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      request,
	})
}

// NotifyResponsiveState sends responsive mode panel state to the overlay.
func (n *OverlayNotifier) NotifyResponsiveState(proxyID string, state *ResponsiveState) error {
	return n.send(OverlayEvent{
		Type:      "responsive_state",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      state,
	})
}

// NotifyInteraction sends an interaction event to the overlay.
func (n *OverlayNotifier) NotifyInteraction(proxyID string, interaction *InteractionEvent) error {
	return n.send(OverlayEvent{
		Type:      "interaction",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      interaction,
	})
}

// NotifyCustom sends a custom event to the overlay.
func (n *OverlayNotifier) NotifyCustom(proxyID, eventType string, data interface{}) error {
	return n.send(OverlayEvent{
		Type:      eventType,
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      data,
	})
}

// NotifyBrowserError sends a browser JS error to the overlay for auto-forwarding.
func (n *OverlayNotifier) NotifyBrowserError(proxyID string, err FrontendError) error {
	return n.send(OverlayEvent{
		Type:      "browser_error",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      err,
	})
}

// NotifyHTTPError sends an HTTP error (4xx/5xx) to the overlay for auto-forwarding.
func (n *OverlayNotifier) NotifyHTTPError(proxyID string, entry HTTPLogEntry) error {
	return n.send(OverlayEvent{
		Type:      "http_error",
		ProxyID:   proxyID,
		Timestamp: time.Now(),
		Data:      entry,
	})
}

func (n *OverlayNotifier) send(event OverlayEvent) error {
	n.mu.RLock()
	if !n.enabled || n.client == nil {
		n.mu.RUnlock()
		debug.Log("overlay-notifier", "event %s dropped: notifier not enabled (enabled=%v, client=%v, socketPath=%q)",
			event.Type, n.enabled, n.client != nil, n.socketPath)
		return nil
	}
	client := n.client
	socketPath := n.socketPath
	n.mu.RUnlock()

	data, err := json.Marshal(event)
	if err != nil {
		return err
	}

	debug.Log("overlay-notifier", "sending %s event to overlay socket %s (%d bytes)", event.Type, socketPath, len(data))

	// Use dummy host - actual connection is via Unix socket
	resp, err := client.Post("http://localhost/event", "application/json", bytes.NewReader(data))
	if err != nil {
		debug.Log("overlay-notifier", "failed to POST %s event to overlay socket %s: %v", event.Type, socketPath, err)
		return err
	}
	defer resp.Body.Close()

	debug.Log("overlay-notifier", "%s event delivered to overlay (status=%d)", event.Type, resp.StatusCode)
	return nil
}

// TypeToOverlay sends a type command to the overlay.
// The overlay will inject this text into the PTY.
func (n *OverlayNotifier) TypeToOverlay(text string, enter bool) error {
	n.mu.RLock()
	if !n.enabled || n.client == nil {
		n.mu.RUnlock()
		return nil
	}
	client := n.client
	n.mu.RUnlock()

	data, err := json.Marshal(map[string]interface{}{
		"text":    text,
		"enter":   enter,
		"instant": true,
	})
	if err != nil {
		return err
	}

	// Use dummy host - actual connection is via Unix socket
	resp, err := client.Post("http://localhost/type", "application/json", bytes.NewReader(data))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return nil
}

// SendKeyToOverlay sends a key to the overlay.
func (n *OverlayNotifier) SendKeyToOverlay(key string, ctrl, alt, shift bool) error {
	n.mu.RLock()
	if !n.enabled || n.client == nil {
		n.mu.RUnlock()
		return nil
	}
	client := n.client
	n.mu.RUnlock()

	data, err := json.Marshal(map[string]interface{}{
		"key":   key,
		"ctrl":  ctrl,
		"alt":   alt,
		"shift": shift,
	})
	if err != nil {
		return err
	}

	// Use dummy host - actual connection is via Unix socket
	resp, err := client.Post("http://localhost/key", "application/json", bytes.NewReader(data))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return nil
}
