package desktop

import (
	"bytes"
	"compress/zlib"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"image"
	"image/jpeg"
	"log"
	"sync"
	"time"

	"github.com/kbinani/screenshot"
	"github.com/pion/webrtc/v4"
)

// WebRTCStreamer - WebRTC Data Channel tabanlı ekran streaming
// Video track yerine Data Channel kullanır (codec bağımlılığı yok)
type WebRTCStreamer struct {
	mu sync.RWMutex

	// Peer connection
	peerConnection *webrtc.PeerConnection
	dataChannel    *webrtc.DataChannel

	// Screen capture
	screenCapture *ScreenCapture

	// Dirty rectangle detection
	lastFrame    *image.RGBA
	lastFrameSum uint64

	// State
	isStreaming bool
	stopCh      chan struct{}

	// Callbacks
	onICECandidate    func(candidate string)
	onConnectionState func(state string)
	onDataMessage     func(message []byte) // Panel'den gelen mesajlar

	// Config
	config WebRTCConfig

	// Stats
	framesSent  uint64
	bytesSent   uint64
	lastFPSTime time.Time
	fpsCount    int
	currentFPS  int
}

// WebRTCConfig - WebRTC ayarları
type WebRTCConfig struct {
	Quality       int     `json:"quality"`       // JPEG kalitesi (1-100)
	FPS           int     `json:"fps"`           // Frame per second
	Scale         float64 `json:"scale"`         // Ölçeklendirme
	DisplayID     int     `json:"displayId"`     // Ekran ID
	UseDirtyRects bool    `json:"useDirtyRects"` // Dirty rectangle optimizasyonu
	Compression   bool    `json:"compression"`   // zlib sıkıştırma
}

// WebRTCSignal - Signaling mesajı
type WebRTCSignal struct {
	Type      string `json:"type"`      // offer, answer, candidate
	SDP       string `json:"sdp"`       // SDP verisi
	Candidate string `json:"candidate"` // ICE candidate JSON
}

// ScreenDataMessage - Ekran verisi mesajı
type ScreenDataMessage struct {
	Type       string `json:"type"`       // "frame", "region", "cursor"
	Data       string `json:"data"`       // base64 encoded image data
	Width      int    `json:"width"`      // Genişlik
	Height     int    `json:"height"`     // Yükseklik
	X          int    `json:"x"`          // Region X (dirty rect için)
	Y          int    `json:"y"`          // Region Y
	Timestamp  int64  `json:"timestamp"`  // Unix ms
	FrameNum   uint64 `json:"frameNum"`   // Frame numarası
	Compressed bool   `json:"compressed"` // zlib ile sıkıştırılmış mı
}

// NewWebRTCStreamer - Yeni WebRTC streamer oluştur
func NewWebRTCStreamer(screenCapture *ScreenCapture) *WebRTCStreamer {
	return &WebRTCStreamer{
		screenCapture: screenCapture,
		config: WebRTCConfig{
			Quality:       60,
			FPS:           20,
			Scale:         0.5,
			DisplayID:     0,
			UseDirtyRects: true,
			Compression:   true,
		},
		lastFPSTime: time.Now(),
	}
}

// SetCallbacks - Callback'leri ayarla
func (w *WebRTCStreamer) SetCallbacks(onICE func(string), onState func(string), onData func([]byte)) {
	w.mu.Lock()
	defer w.mu.Unlock()
	w.onICECandidate = onICE
	w.onConnectionState = onState
	w.onDataMessage = onData
}

// CreateOffer - SDP offer oluştur (ajan başlatıyor)
func (w *WebRTCStreamer) CreateOffer(config *WebRTCConfig) (*WebRTCSignal, error) {
	w.mu.Lock()
	defer w.mu.Unlock()

	if config != nil {
		w.config = *config
	}

	// Peer connection oluştur
	if err := w.createPeerConnection(); err != nil {
		return nil, fmt.Errorf("peer connection oluşturulamadı: %v", err)
	}

	// Data channel oluştur
	if err := w.createDataChannel(); err != nil {
		return nil, fmt.Errorf("data channel oluşturulamadı: %v", err)
	}

	// Offer oluştur
	offer, err := w.peerConnection.CreateOffer(nil)
	if err != nil {
		return nil, fmt.Errorf("offer oluşturulamadı: %v", err)
	}

	// Local description ayarla
	if err := w.peerConnection.SetLocalDescription(offer); err != nil {
		return nil, fmt.Errorf("local description ayarlanamadı: %v", err)
	}

	// ICE gathering tamamlanana kadar bekle
	gatherComplete := webrtc.GatheringCompletePromise(w.peerConnection)
	select {
	case <-gatherComplete:
	case <-time.After(10 * time.Second):
		log.Println("ICE gathering timeout")
	}

	localDesc := w.peerConnection.LocalDescription()
	return &WebRTCSignal{
		Type: "offer",
		SDP:  localDesc.SDP,
	}, nil
}

// HandleAnswer - SDP answer işle (panel'den gelen)
func (w *WebRTCStreamer) HandleAnswer(signal *WebRTCSignal) error {
	w.mu.Lock()
	defer w.mu.Unlock()

	if w.peerConnection == nil {
		return fmt.Errorf("peer connection yok")
	}

	answer := webrtc.SessionDescription{
		Type: webrtc.SDPTypeAnswer,
		SDP:  signal.SDP,
	}

	if err := w.peerConnection.SetRemoteDescription(answer); err != nil {
		return fmt.Errorf("remote description ayarlanamadı: %v", err)
	}

	return nil
}

// HandleOffer - SDP offer işle (panel başlatıyorsa)
func (w *WebRTCStreamer) HandleOffer(signal *WebRTCSignal, config *WebRTCConfig) (*WebRTCSignal, error) {
	w.mu.Lock()
	defer w.mu.Unlock()

	if config != nil {
		w.config = *config
	}

	// Peer connection oluştur
	if err := w.createPeerConnection(); err != nil {
		return nil, fmt.Errorf("peer connection oluşturulamadı: %v", err)
	}

	// Remote description ayarla
	offer := webrtc.SessionDescription{
		Type: webrtc.SDPTypeOffer,
		SDP:  signal.SDP,
	}
	if err := w.peerConnection.SetRemoteDescription(offer); err != nil {
		return nil, fmt.Errorf("remote description ayarlanamadı: %v", err)
	}

	// Answer oluştur
	answer, err := w.peerConnection.CreateAnswer(nil)
	if err != nil {
		return nil, fmt.Errorf("answer oluşturulamadı: %v", err)
	}

	// Local description ayarla
	if err := w.peerConnection.SetLocalDescription(answer); err != nil {
		return nil, fmt.Errorf("local description ayarlanamadı: %v", err)
	}

	// ICE gathering tamamlanana kadar bekle
	gatherComplete := webrtc.GatheringCompletePromise(w.peerConnection)
	select {
	case <-gatherComplete:
	case <-time.After(10 * time.Second):
		log.Println("ICE gathering timeout")
	}

	localDesc := w.peerConnection.LocalDescription()
	return &WebRTCSignal{
		Type: "answer",
		SDP:  localDesc.SDP,
	}, nil
}

// AddICECandidate - ICE candidate ekle
func (w *WebRTCStreamer) AddICECandidate(candidateJSON string) error {
	w.mu.Lock()
	defer w.mu.Unlock()

	if w.peerConnection == nil {
		return fmt.Errorf("peer connection yok")
	}

	var candidate webrtc.ICECandidateInit
	if err := json.Unmarshal([]byte(candidateJSON), &candidate); err != nil {
		return fmt.Errorf("ICE candidate parse edilemedi: %v", err)
	}

	if err := w.peerConnection.AddICECandidate(candidate); err != nil {
		return fmt.Errorf("ICE candidate eklenemedi: %v", err)
	}

	return nil
}

// Stop - Streaming durdur
func (w *WebRTCStreamer) Stop() {
	w.mu.Lock()
	defer w.mu.Unlock()

	w.isStreaming = false
	if w.stopCh != nil {
		close(w.stopCh)
		w.stopCh = nil
	}

	if w.dataChannel != nil {
		w.dataChannel.Close()
		w.dataChannel = nil
	}

	if w.peerConnection != nil {
		w.peerConnection.Close()
		w.peerConnection = nil
	}

	w.lastFrame = nil
	log.Println("WebRTC streaming durduruldu")
}

// IsStreaming - Streaming aktif mi
func (w *WebRTCStreamer) IsStreaming() bool {
	w.mu.RLock()
	defer w.mu.RUnlock()
	return w.isStreaming
}

// UpdateConfig - Ayarları güncelle
func (w *WebRTCStreamer) UpdateConfig(config *WebRTCConfig) {
	w.mu.Lock()
	defer w.mu.Unlock()

	if config.Quality > 0 && config.Quality <= 100 {
		w.config.Quality = config.Quality
	}
	if config.FPS > 0 && config.FPS <= 60 {
		w.config.FPS = config.FPS
	}
	if config.Scale > 0 && config.Scale <= 1 {
		w.config.Scale = config.Scale
	}
	if config.DisplayID >= 0 {
		w.config.DisplayID = config.DisplayID
	}
	w.config.UseDirtyRects = config.UseDirtyRects
	w.config.Compression = config.Compression
}

// createPeerConnection - Peer connection oluştur
func (w *WebRTCStreamer) createPeerConnection() error {
	// ICE servers - STUN/TURN
	config := webrtc.Configuration{
		ICEServers: []webrtc.ICEServer{
			{
				URLs: []string{
					"stun:stun.l.google.com:19302",
					"stun:stun1.l.google.com:19302",
					"stun:stun2.l.google.com:19302",
				},
			},
		},
	}

	// Peer connection oluştur
	pc, err := webrtc.NewPeerConnection(config)
	if err != nil {
		return err
	}

	// ICE candidate handler
	pc.OnICECandidate(func(candidate *webrtc.ICECandidate) {
		if candidate == nil {
			return
		}

		candidateJSON, err := json.Marshal(candidate.ToJSON())
		if err != nil {
			log.Printf("ICE candidate serialize edilemedi: %v", err)
			return
		}

		if w.onICECandidate != nil {
			w.onICECandidate(string(candidateJSON))
		}
	})

	// Connection state handler
	pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
		log.Printf("WebRTC connection state: %s", state.String())

		if w.onConnectionState != nil {
			w.onConnectionState(state.String())
		}

		switch state {
		case webrtc.PeerConnectionStateConnected:
			// Bağlantı kuruldu, streaming başlat
			go w.startStreaming()
		case webrtc.PeerConnectionStateFailed,
			webrtc.PeerConnectionStateDisconnected,
			webrtc.PeerConnectionStateClosed:
			// Bağlantı koptu
			go w.Stop()
		}
	})

	// Data channel handler (panel tarafından oluşturulursa)
	pc.OnDataChannel(func(dc *webrtc.DataChannel) {
		log.Printf("Data channel açıldı: %s", dc.Label())
		w.mu.Lock()
		w.dataChannel = dc
		w.mu.Unlock()
		w.setupDataChannelHandlers(dc)
	})

	w.peerConnection = pc
	return nil
}

// createDataChannel - Data channel oluştur (ajan tarafında)
func (w *WebRTCStreamer) createDataChannel() error {
	ordered := true
	maxRetransmits := uint16(0) // Unreliable (daha hızlı)

	dc, err := w.peerConnection.CreateDataChannel("screen", &webrtc.DataChannelInit{
		Ordered:        &ordered,
		MaxRetransmits: &maxRetransmits,
	})
	if err != nil {
		return err
	}

	w.dataChannel = dc
	w.setupDataChannelHandlers(dc)
	return nil
}

// setupDataChannelHandlers - Data channel event handler'ları
func (w *WebRTCStreamer) setupDataChannelHandlers(dc *webrtc.DataChannel) {
	dc.OnOpen(func() {
		log.Println("Data channel açıldı, streaming başlıyor...")
		go w.startStreaming()
	})

	dc.OnClose(func() {
		log.Println("Data channel kapandı")
		w.mu.Lock()
		w.isStreaming = false
		w.mu.Unlock()
	})

	dc.OnMessage(func(msg webrtc.DataChannelMessage) {
		// Panel'den gelen mesajlar (input events, config updates)
		if w.onDataMessage != nil {
			w.onDataMessage(msg.Data)
		}
	})

	dc.OnError(func(err error) {
		log.Printf("Data channel hatası: %v", err)
	})
}

// startStreaming - Streaming başlat
func (w *WebRTCStreamer) startStreaming() {
	w.mu.Lock()
	if w.isStreaming {
		w.mu.Unlock()
		return
	}
	w.isStreaming = true
	w.stopCh = make(chan struct{})
	config := w.config
	w.mu.Unlock()

	log.Printf("WebRTC streaming başlatıldı - FPS: %d, Quality: %d, Scale: %.2f",
		config.FPS, config.Quality, config.Scale)

	interval := time.Second / time.Duration(config.FPS)
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		select {
		case <-w.stopCh:
			return
		case <-ticker.C:
			w.captureAndSend()
		}
	}
}

// captureAndSend - Ekran yakala ve gönder
func (w *WebRTCStreamer) captureAndSend() {
	w.mu.RLock()
	if !w.isStreaming || w.dataChannel == nil || w.dataChannel.ReadyState() != webrtc.DataChannelStateOpen {
		w.mu.RUnlock()
		return
	}
	config := w.config
	w.mu.RUnlock()

	// Ekran görüntüsü yakala
	displayID := config.DisplayID
	n := screenshot.NumActiveDisplays()
	if displayID >= n {
		displayID = 0
	}

	bounds := screenshot.GetDisplayBounds(displayID)
	img, err := screenshot.CaptureRect(bounds)
	if err != nil {
		log.Printf("Ekran yakalanamadı: %v", err)
		return
	}

	// Ölçeklendirme
	var scaledImg image.Image = img
	if config.Scale < 1.0 {
		scaledImg = scaleImageFast(img, config.Scale)
	}

	// Dirty rectangle kontrolü
	if config.UseDirtyRects && w.lastFrame != nil {
		// Basit değişiklik kontrolü (tam dirty rect implementasyonu daha sonra)
		currentSum := w.calculateImageSum(scaledImg)
		if currentSum == w.lastFrameSum {
			// Frame değişmedi, gönderme
			return
		}
		w.lastFrameSum = currentSum
	}

	// JPEG encode
	var buf bytes.Buffer
	if err := jpeg.Encode(&buf, scaledImg, &jpeg.Options{Quality: config.Quality}); err != nil {
		log.Printf("JPEG encode hatası: %v", err)
		return
	}

	imgData := buf.Bytes()

	// Sıkıştırma
	compressed := false
	if config.Compression {
		compressedData := w.compressData(imgData)
		if len(compressedData) < len(imgData) {
			imgData = compressedData
			compressed = true
		}
	}

	// Mesaj oluştur
	w.mu.Lock()
	w.framesSent++
	frameNum := w.framesSent
	w.bytesSent += uint64(len(imgData))

	// FPS hesapla
	w.fpsCount++
	if time.Since(w.lastFPSTime) >= time.Second {
		w.currentFPS = w.fpsCount
		w.fpsCount = 0
		w.lastFPSTime = time.Now()
	}
	w.mu.Unlock()

	message := ScreenDataMessage{
		Type:       "frame",
		Data:       base64.StdEncoding.EncodeToString(imgData),
		Width:      scaledImg.Bounds().Dx(),
		Height:     scaledImg.Bounds().Dy(),
		Timestamp:  time.Now().UnixMilli(),
		FrameNum:   frameNum,
		Compressed: compressed,
	}

	messageJSON, err := json.Marshal(message)
	if err != nil {
		log.Printf("Mesaj serialize edilemedi: %v", err)
		return
	}

	// Gönder
	w.mu.RLock()
	dc := w.dataChannel
	w.mu.RUnlock()

	if dc != nil && dc.ReadyState() == webrtc.DataChannelStateOpen {
		if err := dc.Send(messageJSON); err != nil {
			log.Printf("Frame gönderilemedi: %v", err)
		}
	}
}

// calculateImageSum - Basit image checksum (değişiklik tespiti için)
func (w *WebRTCStreamer) calculateImageSum(img image.Image) uint64 {
	bounds := img.Bounds()
	var sum uint64

	// Her 10 pikselden birini örnekle (hız için)
	for y := bounds.Min.Y; y < bounds.Max.Y; y += 10 {
		for x := bounds.Min.X; x < bounds.Max.X; x += 10 {
			r, g, b, _ := img.At(x, y).RGBA()
			sum += uint64(r + g + b)
		}
	}

	return sum
}

// compressData - zlib sıkıştırma
func (w *WebRTCStreamer) compressData(data []byte) []byte {
	var buf bytes.Buffer
	writer := zlib.NewWriter(&buf)
	writer.Write(data)
	writer.Close()
	return buf.Bytes()
}

// GetStats - WebRTC istatistikleri
func (w *WebRTCStreamer) GetStats() map[string]interface{} {
	w.mu.RLock()
	defer w.mu.RUnlock()

	stats := map[string]interface{}{
		"isStreaming": w.isStreaming,
		"config":      w.config,
		"framesSent":  w.framesSent,
		"bytesSent":   w.bytesSent,
		"currentFPS":  w.currentFPS,
	}

	if w.peerConnection != nil {
		stats["connectionState"] = w.peerConnection.ConnectionState().String()
		stats["iceConnectionState"] = w.peerConnection.ICEConnectionState().String()
		stats["signalingState"] = w.peerConnection.SignalingState().String()
	}

	if w.dataChannel != nil {
		stats["dataChannelState"] = w.dataChannel.ReadyState().String()
		stats["bufferedAmount"] = w.dataChannel.BufferedAmount()
	}

	return stats
}

// SendInputEvent - Panel'den gelen input event'i gönder
func (w *WebRTCStreamer) SendInputEvent(eventType string, data map[string]interface{}) error {
	w.mu.RLock()
	dc := w.dataChannel
	w.mu.RUnlock()

	if dc == nil || dc.ReadyState() != webrtc.DataChannelStateOpen {
		return fmt.Errorf("data channel açık değil")
	}

	message := map[string]interface{}{
		"type": eventType,
		"data": data,
	}

	messageJSON, err := json.Marshal(message)
	if err != nil {
		return err
	}

	return dc.Send(messageJSON)
}
