package notification

import (
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/wailsapp/wails/v3/pkg/application"
)

// WindowController - Pencere kontrol interface'i
type WindowController interface {
	OnNotificationAdded()
	OnNotificationRemoved()
}

// BildirimOlayi - Bildirim olay tipi
type BildirimOlayi struct {
	Tip          string                 `json:"tip"`          // gosterildi, kapatildi, cevaplandi, guncellendi, timeout, tamamlandi, hata
	BildirimId   string                 `json:"bildirimId"`
	MesajId      string                 `json:"mesajId"`      // Sunucudan gelen mesaj ID'si
	Baslik       string                 `json:"baslik"`
	Icerik       string                 `json:"icerik"`
	BildirimTipi string                 `json:"bildirimTipi"` // toast, dialog, progress
	Cevap        string                 `json:"cevap"`        // Kullanıcı cevabı (dialog için)
	KapatmaTipi  string                 `json:"kapatmaTipi"`  // otomatik, kullanici, sistem
	Progress     float64                `json:"progress"`     // Progress değeri
	Sure         int64                  `json:"sure"`         // Gösterim süresi (ms)
	Zaman        string                 `json:"zaman"`
	EkVeri       map[string]interface{} `json:"ekVeri"`
}

// BildirimOlayCallback - Bildirim olay callback tipi
type BildirimOlayCallback func(olay BildirimOlayi)

// Manager - Bildirim yöneticisi
type Manager struct {
	// Wails v3 application
	wailsApp *application.App

	// Bildirim penceresi
	notificationWindow *application.WebviewWindow

	// Aktif bildirimler
	notifications sync.Map

	// Callback'ler (mesaj cevabı için)
	callbacks sync.Map

	// Mesaj ID eşleştirme (bildirimId -> mesajId)
	mesajIdMap sync.Map

	// Bildirim gösterim zamanları (bildirimId -> time.Time)
	gosterimZamanlari sync.Map

	// ID sayacı
	idCounter int64
	idMu      sync.Mutex

	// Pencere kontrol
	windowController WindowController

	// Dış olay callback'i (socket'e göndermek için)
	olayCallback BildirimOlayCallback
}

// ActionCallback - Aksiyon callback tipi
type ActionCallback func(buttonId string, buttonLabel string)

// NewManagerV3 - Wails v3 için yeni bildirim yöneticisi
func NewManagerV3(app *application.App, notifWindow *application.WebviewWindow) *Manager {
	return &Manager{
		wailsApp:           app,
		notificationWindow: notifWindow,
	}
}

// SetWindowController - Pencere controller'ı ayarla
func (m *Manager) SetWindowController(wc WindowController) {
	m.windowController = wc
}

// SetOlayCallback - Dış olay callback'ini ayarla (socket'e göndermek için)
func (m *Manager) SetOlayCallback(callback BildirimOlayCallback) {
	m.olayCallback = callback
}

// emitOlay - Bildirim olayını dış callback'e gönder
func (m *Manager) emitOlay(olay BildirimOlayi) {
	if m.olayCallback != nil {
		m.olayCallback(olay)
	}
}

// generateID - Benzersiz ID oluştur
func (m *Manager) generateID() string {
	m.idMu.Lock()
	defer m.idMu.Unlock()
	m.idCounter++
	return fmt.Sprintf("notif_%d_%d", time.Now().UnixNano(), m.idCounter)
}

// Show - Yeni bildirim göster
func (m *Manager) Show(n *Notification) string {
	if n.ID == "" {
		n.ID = m.generateID()
	}
	if n.CreatedAt.IsZero() {
		n.CreatedAt = time.Now()
	}
	if n.Audio == "" {
		n.Audio = AudioDefault
	}
	if n.Level == "" {
		n.Level = LevelInfo
	}

	// Bildirimi kaydet
	m.notifications.Store(n.ID, n)

	// Gösterim zamanını kaydet
	m.gosterimZamanlari.Store(n.ID, time.Now())

	// Frontend'e event gönder
	m.emitEvent("notification:show", n)

	// Pencereyi göster
	if m.windowController != nil {
		m.windowController.OnNotificationAdded()
	}

	log.Printf("Bildirim gösterildi: %s - %s", n.ID, n.Title)

	// Dış olay gönder - "gosterildi"
	mesajId, _ := m.mesajIdMap.Load(n.ID)
	mesajIdStr, _ := mesajId.(string)
	m.emitOlay(BildirimOlayi{
		Tip:          "gosterildi",
		BildirimId:   n.ID,
		MesajId:      mesajIdStr,
		Baslik:       n.Title,
		Icerik:       n.Message,
		BildirimTipi: string(n.Type),
		Zaman:        time.Now().Format(time.RFC3339),
	})

	return n.ID
}

// ShowWithMesajId - Mesaj ID ile birlikte bildirim göster
func (m *Manager) ShowWithMesajId(n *Notification, mesajId string) string {
	if n.ID == "" {
		n.ID = m.generateID()
	}
	// Mesaj ID'yi kaydet
	if mesajId != "" {
		m.mesajIdMap.Store(n.ID, mesajId)
	}
	return m.Show(n)
}

// ShowToast - Basit toast bildirimi göster
func (m *Manager) ShowToast(title, message string, level NotificationLevel) string {
	n := &Notification{
		Type:     TypeToast,
		Level:    level,
		Title:    title,
		Message:  message,
		Duration: "short",
		Audio:    AudioDefault,
	}
	return m.Show(n)
}

// ShowDialog - Dialog bildirimi göster
func (m *Manager) ShowDialog(title, message string, buttons []string, callback ActionCallback) string {
	btns := make([]Button, len(buttons))
	for i, label := range buttons {
		btns[i] = Button{
			ID:      fmt.Sprintf("btn_%d", i),
			Label:   label,
			Primary: i == 0,
		}
	}

	n := &Notification{
		Type:     TypeDialog,
		Level:    LevelInfo,
		Title:    title,
		Message:  message,
		Buttons:  btns,
		Duration: "persistent",
		Wait:     true,
		Audio:    AudioIM,
	}

	id := m.Show(n)

	// Callback'i kaydet
	if callback != nil {
		m.callbacks.Store(id, callback)
	}

	return id
}

// ShowProgress - Progress bildirimi göster
func (m *Manager) ShowProgress(title, text string, progress float64) string {
	n := &Notification{
		Type:           TypeProgress,
		Level:          LevelInfo,
		Title:          title,
		Message:        text,
		Progress:       progress,
		ProgressText:   fmt.Sprintf("%.0f%%", progress*100),
		ProgressStatus: "running",
		Duration:       "persistent",
		Audio:          AudioSilent,
	}
	return m.Show(n)
}

// UpdateProgress - Progress bildirimini güncelle
func (m *Manager) UpdateProgress(id string, progress float64, text string) {
	if val, ok := m.notifications.Load(id); ok {
		n := val.(*Notification)
		n.Progress = progress
		n.ProgressText = fmt.Sprintf("%.0f%%", progress*100)
		if text != "" {
			n.Message = text
		}

		// Frontend'e güncelleme gönder
		m.emitEvent("notification:update", n)

		log.Printf("Progress güncellendi: %s - %.0f%%", id, progress*100)

		// Mesaj ID'yi al
		mesajId, _ := m.mesajIdMap.Load(id)
		mesajIdStr, _ := mesajId.(string)

		// Dış olay gönder - "guncellendi"
		m.emitOlay(BildirimOlayi{
			Tip:          "guncellendi",
			BildirimId:   id,
			MesajId:      mesajIdStr,
			Baslik:       n.Title,
			Icerik:       text,
			BildirimTipi: string(n.Type),
			Progress:     progress,
			Zaman:        time.Now().Format(time.RFC3339),
		})
	}
}

// CompleteProgress - Progress bildirimini tamamla
func (m *Manager) CompleteProgress(id string, message string, success bool) {
	if val, ok := m.notifications.Load(id); ok {
		n := val.(*Notification)
		n.Progress = 1.0
		n.ProgressText = "100%"
		if message != "" {
			n.Message = message
		}

		if success {
			n.ProgressStatus = "completed"
			n.Level = LevelSuccess
			n.Audio = AudioSMS
		} else {
			n.ProgressStatus = "error"
			n.Level = LevelError
			n.Audio = AudioReminder
		}

		// Frontend'e güncelleme gönder
		m.emitEvent("notification:update", n)

		// Mesaj ID'yi al
		mesajId, _ := m.mesajIdMap.Load(id)
		mesajIdStr, _ := mesajId.(string)

		// Gösterim süresini hesapla
		var sure int64 = 0
		if gosterimZamani, ok := m.gosterimZamanlari.Load(id); ok {
			sure = time.Since(gosterimZamani.(time.Time)).Milliseconds()
		}

		// Dış olay gönder - "tamamlandi" veya "hata"
		olayTip := "tamamlandi"
		if !success {
			olayTip = "hata"
		}
		m.emitOlay(BildirimOlayi{
			Tip:          olayTip,
			BildirimId:   id,
			MesajId:      mesajIdStr,
			Baslik:       n.Title,
			Icerik:       message,
			BildirimTipi: string(n.Type),
			Progress:     1.0,
			Sure:         sure,
			Zaman:        time.Now().Format(time.RFC3339),
		})

		// 3 saniye sonra otomatik kapat
		go func() {
			time.Sleep(3 * time.Second)
			m.DismissWithType(id, "otomatik")
		}()

		log.Printf("Progress tamamlandı: %s - success=%v", id, success)
	}
}

// Dismiss - Bildirimi kapat (otomatik timeout)
func (m *Manager) Dismiss(id string) {
	m.DismissWithType(id, "otomatik")
}

// DismissWithType - Bildirimi kapatma tipi ile kapat
func (m *Manager) DismissWithType(id string, kapatmaTipi string) {
	// Bildirim bilgilerini al (olay göndermek için)
	var baslik, icerik, bildirimTipi string
	if val, ok := m.notifications.Load(id); ok {
		n := val.(*Notification)
		baslik = n.Title
		icerik = n.Message
		bildirimTipi = string(n.Type)
	}

	// Gösterim süresini hesapla
	var sure int64 = 0
	if gosterimZamani, ok := m.gosterimZamanlari.Load(id); ok {
		sure = time.Since(gosterimZamani.(time.Time)).Milliseconds()
		m.gosterimZamanlari.Delete(id)
	}

	// Mesaj ID'yi al
	mesajId, _ := m.mesajIdMap.Load(id)
	mesajIdStr, _ := mesajId.(string)

	m.notifications.Delete(id)
	m.callbacks.Delete(id)
	m.mesajIdMap.Delete(id)

	// Frontend'e event gönder
	m.emitEvent("notification:dismiss", map[string]string{"id": id})

	// Pencereyi gizle (tüm bildirimler kapandıysa)
	if m.windowController != nil {
		m.windowController.OnNotificationRemoved()
	}

	log.Printf("Bildirim kapatıldı: %s (tip: %s)", id, kapatmaTipi)

	// Dış olay gönder - "kapatildi"
	m.emitOlay(BildirimOlayi{
		Tip:          "kapatildi",
		BildirimId:   id,
		MesajId:      mesajIdStr,
		Baslik:       baslik,
		Icerik:       icerik,
		BildirimTipi: bildirimTipi,
		KapatmaTipi:  kapatmaTipi,
		Sure:         sure,
		Zaman:        time.Now().Format(time.RFC3339),
	})
}

// Clear - Tüm bildirimleri temizle
func (m *Manager) Clear() {
	m.notifications.Range(func(key, value interface{}) bool {
		m.notifications.Delete(key)
		m.callbacks.Delete(key)
		return true
	})

	m.emitEvent("notification:clear", nil)

	log.Println("Tüm bildirimler temizlendi")
}

// HandleAction - Frontend'den gelen aksiyon işle
func (m *Manager) HandleAction(notificationId string, buttonId string) {
	log.Printf("Aksiyon alındı: %s - %s", notificationId, buttonId)

	// Bildirim bilgilerini al
	var baslik, icerik, bildirimTipi string
	buttonLabel := buttonId
	if n, ok := m.notifications.Load(notificationId); ok {
		notification := n.(*Notification)
		baslik = notification.Title
		icerik = notification.Message
		bildirimTipi = string(notification.Type)
		for _, btn := range notification.Buttons {
			if btn.ID == buttonId {
				buttonLabel = btn.Label
				break
			}
		}
	}

	// Gösterim süresini hesapla
	var sure int64 = 0
	if gosterimZamani, ok := m.gosterimZamanlari.Load(notificationId); ok {
		sure = time.Since(gosterimZamani.(time.Time)).Milliseconds()
	}

	// Mesaj ID'yi al
	mesajId, _ := m.mesajIdMap.Load(notificationId)
	mesajIdStr, _ := mesajId.(string)

	// Callback'i çağır
	if val, ok := m.callbacks.Load(notificationId); ok {
		if callback, ok := val.(ActionCallback); ok {
			callback(buttonId, buttonLabel)
		}
	}

	// Dış olay gönder - "cevaplandi"
	m.emitOlay(BildirimOlayi{
		Tip:          "cevaplandi",
		BildirimId:   notificationId,
		MesajId:      mesajIdStr,
		Baslik:       baslik,
		Icerik:       icerik,
		BildirimTipi: bildirimTipi,
		Cevap:        buttonLabel,
		KapatmaTipi:  "kullanici",
		Sure:         sure,
		Zaman:        time.Now().Format(time.RFC3339),
	})

	// Bildirimi kapat (kullanıcı tarafından)
	m.DismissWithType(notificationId, "kullanici")
}

// GetAll - Tüm aktif bildirimleri getir
func (m *Manager) GetAll() []*Notification {
	var notifications []*Notification
	m.notifications.Range(func(key, value interface{}) bool {
		notifications = append(notifications, value.(*Notification))
		return true
	})
	return notifications
}

// emitEvent - Frontend'e event gönder (Wails v3)
func (m *Manager) emitEvent(eventName string, data interface{}) {
	if m.notificationWindow == nil {
		log.Printf("NotificationWindow nil, event gönderilemedi: %s", eventName)
		return
	}

	// Debug: Gönderilen data'yı logla
	if n, ok := data.(*Notification); ok {
		log.Printf("Event gönderiliyor: %s - ID: %s, Title: %s, Message: %s, Type: %s, Buttons: %d",
			eventName, n.ID, n.Title, n.Message, n.Type, len(n.Buttons))

		// Pointer yerine map olarak gönder (daha güvenilir serialization)
		notifMap := map[string]interface{}{
			"id":             n.ID,
			"type":           string(n.Type),
			"level":          string(n.Level),
			"title":          n.Title,
			"message":        n.Message,
			"icon":           n.Icon,
			"image":          n.Image,
			"audio":          string(n.Audio),
			"duration":       n.Duration,
			"wait":           n.Wait,
			"progress":       n.Progress,
			"progressText":   n.ProgressText,
			"progressStatus": n.ProgressStatus,
			"createdAt":      n.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
		}

		// Buttons varsa ekle
		if len(n.Buttons) > 0 {
			buttons := make([]map[string]interface{}, len(n.Buttons))
			for i, btn := range n.Buttons {
				buttons[i] = map[string]interface{}{
					"id":      btn.ID,
					"label":   btn.Label,
					"primary": btn.Primary,
				}
			}
			notifMap["buttons"] = buttons
		}

		m.notificationWindow.EmitEvent(eventName, notifMap)
		return
	}

	// Wails v3: Pencereye özel event gönder
	m.notificationWindow.EmitEvent(eventName, data)
}

// === Mesaj handler için yardımcı metodlar ===

// DismissManual - Kullanıcı tarafından manuel kapatma
func (m *Manager) DismissManual(id string) {
	m.DismissWithType(id, "kullanici")
}

// ShowFromData - Map data'dan bildirim göster
func (m *Manager) ShowFromData(data map[string]interface{}) string {
	n := &Notification{
		CreatedAt: time.Now(),
	}

	// Mesaj ID'yi al ve kaydet (varsa)
	mesajId, _ := data["mesajId"].(string)

	// Tip
	if tip, ok := data["tip"].(string); ok {
		switch tip {
		case "toast":
			n.Type = TypeToast
		case "dialog":
			n.Type = TypeDialog
		case "progress":
			n.Type = TypeProgress
		default:
			n.Type = TypeToast
		}
	} else {
		n.Type = TypeToast
	}

	// Temel alanlar
	if baslik, ok := data["baslik"].(string); ok {
		n.Title = baslik
	}
	if icerik, ok := data["icerik"].(string); ok {
		n.Message = icerik
	}
	if resim, ok := data["resim"].(string); ok {
		n.Image = resim
	}
	if ikon, ok := data["ikon"].(string); ok {
		n.Icon = ikon
	}

	// Ses
	if ses, ok := data["ses"].(string); ok {
		switch ses {
		case "im", "IM", "mesaj":
			n.Audio = AudioIM
		case "mail", "Mail", "eposta":
			n.Audio = AudioMail
		case "reminder", "Reminder", "hatirlatici":
			n.Audio = AudioReminder
		case "sms", "SMS":
			n.Audio = AudioSMS
		case "silent", "Silent", "sessiz":
			n.Audio = AudioSilent
		default:
			n.Audio = AudioDefault
		}
	} else {
		n.Audio = AudioDefault
	}

	// Süre
	if sure, ok := data["sure"].(string); ok {
		switch sure {
		case "uzun", "long":
			n.Duration = "long"
		case "bekle", "persistent":
			n.Duration = "persistent"
			n.Wait = true
		default:
			n.Duration = "short"
		}
	} else {
		n.Duration = "short"
	}

	// Bekle
	if bekle, ok := data["bekle"].(bool); ok && bekle {
		n.Wait = true
		n.Duration = "persistent"
	}

	// Butonlar - hem string[] hem de [{text, renk, ikon}] formatını destekle
	if butonlar, ok := data["butonlar"].([]interface{}); ok && len(butonlar) > 0 {
		for i, b := range butonlar {
			switch v := b.(type) {
			case string:
				// Eski format: string dizisi ["Tamam", "İptal"]
				n.Buttons = append(n.Buttons, Button{
					ID:      fmt.Sprintf("btn_%d", i),
					Label:   v,
					Primary: i == 0,
				})
			case map[string]interface{}:
				// Yeni format: {text, renk, ikon} nesneleri
				label, _ := v["text"].(string)
				renk, _ := v["renk"].(string)
				ikon, _ := v["ikon"].(string)
				if label == "" {
					label = fmt.Sprintf("Buton %d", i+1)
				}
				isPrimary := i == 0
				if renk == "primary" {
					isPrimary = true
				}
				n.Buttons = append(n.Buttons, Button{
					ID:      fmt.Sprintf("btn_%d", i),
					Label:   label,
					Primary: isPrimary,
					Icon:    ikon,
					Renk:    renk,
				})
			}
		}
	}

	// Progress için
	if n.Type == TypeProgress {
		if deger, ok := data["deger"].(float64); ok {
			n.Progress = deger
			n.ProgressText = fmt.Sprintf("%.0f%%", deger*100)
		}
		if aciklama, ok := data["aciklama"].(string); ok {
			n.Message = aciklama
		}
		n.ProgressStatus = "running"
		n.Audio = AudioSilent
	}

	// Mesaj ID ile birlikte göster
	return m.ShowWithMesajId(n, mesajId)
}
