package plugin

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"plugin"
	"runtime"
	"sync"
	"time"
)

// PluginManager - Plugin yönetim servisi
type PluginManager struct {
	mu              sync.RWMutex
	plugins         map[string]*Plugin
	pluginDir       string
	configDir       string
	eventHandlers   map[string][]PluginEventHandler
	sandboxEnabled  bool
	allowedPlugins  []string // Beyaz liste
	blockedPlugins  []string // Kara liste
	maxPluginMem    int64    // Max memory per plugin
	maxPluginCPU    float64  // Max CPU per plugin
}

// Plugin - Plugin yapısı
type Plugin struct {
	ID           string            `json:"id"`
	Name         string            `json:"name"`
	Version      string            `json:"version"`
	Description  string            `json:"description"`
	Author       string            `json:"author"`
	License      string            `json:"license"`
	Homepage     string            `json:"homepage"`
	Icon         string            `json:"icon"`
	Category     string            `json:"category"`
	Tags         []string          `json:"tags"`
	Permissions  []string          `json:"permissions"` // İzinler
	Dependencies []PluginDep       `json:"dependencies"`
	Config       map[string]interface{} `json:"config"`

	// Runtime bilgileri
	Status       PluginStatus      `json:"status"`
	LoadedAt     time.Time         `json:"loadedAt"`
	Path         string            `json:"path"`
	EntryPoint   string            `json:"entryPoint"`
	Type         PluginType        `json:"type"` // "native", "script", "wasm"
	Hash         string            `json:"hash"` // Bütünlük kontrolü

	// İç yapı
	instance     interface{}
	process      *exec.Cmd
	stopChan     chan struct{}
}

// PluginDep - Plugin bağımlılığı
type PluginDep struct {
	ID      string `json:"id"`
	Version string `json:"version"`
}

// PluginStatus - Plugin durumu
type PluginStatus string

const (
	PluginStatusUnloaded  PluginStatus = "unloaded"
	PluginStatusLoading   PluginStatus = "loading"
	PluginStatusLoaded    PluginStatus = "loaded"
	PluginStatusRunning   PluginStatus = "running"
	PluginStatusStopped   PluginStatus = "stopped"
	PluginStatusError     PluginStatus = "error"
	PluginStatusDisabled  PluginStatus = "disabled"
)

// PluginType - Plugin tipi
type PluginType string

const (
	PluginTypeNative PluginType = "native" // Go plugin (.so)
	PluginTypeScript PluginType = "script" // Script (JS/Lua)
	PluginTypeWASM   PluginType = "wasm"   // WebAssembly
	PluginTypeExec   PluginType = "exec"   // Executable
)

// PluginEventHandler - Plugin event handler
type PluginEventHandler func(event string, data interface{})

// PluginInterface - Plugin arayüzü (native pluginler için)
type PluginInterface interface {
	Init(config map[string]interface{}) error
	Start() error
	Stop() error
	HandleEvent(event string, data interface{}) interface{}
	GetInfo() PluginInfo
}

// PluginInfo - Plugin bilgisi
type PluginInfo struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	Version     string   `json:"version"`
	Description string   `json:"description"`
	Author      string   `json:"author"`
	Permissions []string `json:"permissions"`
}

// PluginManifest - Plugin manifest dosyası
type PluginManifest struct {
	ID           string                 `json:"id"`
	Name         string                 `json:"name"`
	Version      string                 `json:"version"`
	Description  string                 `json:"description"`
	Author       string                 `json:"author"`
	License      string                 `json:"license"`
	Homepage     string                 `json:"homepage"`
	Icon         string                 `json:"icon"`
	Category     string                 `json:"category"`
	Tags         []string               `json:"tags"`
	EntryPoint   string                 `json:"entryPoint"`
	Type         PluginType             `json:"type"`
	Permissions  []string               `json:"permissions"`
	Dependencies []PluginDep            `json:"dependencies"`
	Config       map[string]interface{} `json:"config"`
	MinAgent     string                 `json:"minAgentVersion"`
	MaxAgent     string                 `json:"maxAgentVersion"`
	Platforms    []string               `json:"platforms"` // windows, linux, darwin
}

// NewPluginManager - Yeni plugin yöneticisi
func NewPluginManager(pluginDir, configDir string) *PluginManager {
	pm := &PluginManager{
		plugins:        make(map[string]*Plugin),
		pluginDir:      pluginDir,
		configDir:      configDir,
		eventHandlers:  make(map[string][]PluginEventHandler),
		sandboxEnabled: true,
		maxPluginMem:   100 * 1024 * 1024, // 100 MB
		maxPluginCPU:   25.0,               // 25%
	}

	// Plugin dizinini oluştur
	os.MkdirAll(pluginDir, 0755)
	os.MkdirAll(configDir, 0755)

	return pm
}

// DiscoverPlugins - Pluginleri keşfet
func (pm *PluginManager) DiscoverPlugins() ([]PluginManifest, error) {
	var manifests []PluginManifest

	entries, err := os.ReadDir(pm.pluginDir)
	if err != nil {
		return nil, err
	}

	for _, entry := range entries {
		if !entry.IsDir() {
			continue
		}

		manifestPath := filepath.Join(pm.pluginDir, entry.Name(), "manifest.json")
		if _, err := os.Stat(manifestPath); os.IsNotExist(err) {
			continue
		}

		manifest, err := pm.loadManifest(manifestPath)
		if err != nil {
			log.Printf("Plugin manifest yüklenemedi: %s - %v", entry.Name(), err)
			continue
		}

		// Platform kontrolü
		if !pm.isPlatformSupported(manifest) {
			continue
		}

		manifests = append(manifests, *manifest)
	}

	return manifests, nil
}

// loadManifest - Manifest dosyasını yükle
func (pm *PluginManager) loadManifest(path string) (*PluginManifest, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var manifest PluginManifest
	if err := json.Unmarshal(data, &manifest); err != nil {
		return nil, err
	}

	return &manifest, nil
}

// isPlatformSupported - Platform destekleniyor mu
func (pm *PluginManager) isPlatformSupported(manifest *PluginManifest) bool {
	if len(manifest.Platforms) == 0 {
		return true // Platform belirtilmemişse tüm platformlar
	}

	currentPlatform := runtime.GOOS
	for _, platform := range manifest.Platforms {
		if platform == currentPlatform || platform == "all" {
			return true
		}
	}

	return false
}

// LoadPlugin - Plugin yükle
func (pm *PluginManager) LoadPlugin(pluginID string) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()

	// Zaten yüklü mü?
	if p, exists := pm.plugins[pluginID]; exists && p.Status == PluginStatusLoaded {
		return nil
	}

	// Beyaz/kara liste kontrolü
	if !pm.isPluginAllowed(pluginID) {
		return fmt.Errorf("plugin izin listesinde değil: %s", pluginID)
	}

	pluginPath := filepath.Join(pm.pluginDir, pluginID)
	manifestPath := filepath.Join(pluginPath, "manifest.json")

	manifest, err := pm.loadManifest(manifestPath)
	if err != nil {
		return fmt.Errorf("manifest yüklenemedi: %v", err)
	}

	// Plugin nesnesi oluştur
	p := &Plugin{
		ID:           manifest.ID,
		Name:         manifest.Name,
		Version:      manifest.Version,
		Description:  manifest.Description,
		Author:       manifest.Author,
		License:      manifest.License,
		Homepage:     manifest.Homepage,
		Icon:         manifest.Icon,
		Category:     manifest.Category,
		Tags:         manifest.Tags,
		Permissions:  manifest.Permissions,
		Dependencies: manifest.Dependencies,
		Config:       manifest.Config,
		Path:         pluginPath,
		EntryPoint:   manifest.EntryPoint,
		Type:         manifest.Type,
		Status:       PluginStatusLoading,
		stopChan:     make(chan struct{}),
	}

	// Hash hesapla
	if hash, err := pm.calculatePluginHash(pluginPath); err == nil {
		p.Hash = hash
	}

	// Bağımlılıkları kontrol et
	if err := pm.checkDependencies(manifest.Dependencies); err != nil {
		return fmt.Errorf("bağımlılık hatası: %v", err)
	}

	// Plugin tipine göre yükle
	switch manifest.Type {
	case PluginTypeNative:
		err = pm.loadNativePlugin(p)
	case PluginTypeScript:
		err = pm.loadScriptPlugin(p)
	case PluginTypeExec:
		err = pm.loadExecPlugin(p)
	case PluginTypeWASM:
		err = pm.loadWASMPlugin(p)
	default:
		err = fmt.Errorf("desteklenmeyen plugin tipi: %s", manifest.Type)
	}

	if err != nil {
		p.Status = PluginStatusError
		return err
	}

	// Plugin config yükle
	pm.loadPluginConfig(p)

	p.Status = PluginStatusLoaded
	p.LoadedAt = time.Now()
	pm.plugins[pluginID] = p

	log.Printf("Plugin yüklendi: %s v%s", p.Name, p.Version)
	pm.emitEvent("plugin:loaded", p)

	return nil
}

// loadNativePlugin - Native Go plugin yükle
func (pm *PluginManager) loadNativePlugin(p *Plugin) error {
	if runtime.GOOS == "windows" {
		return fmt.Errorf("native pluginler Windows'ta desteklenmiyor")
	}

	entryPath := filepath.Join(p.Path, p.EntryPoint)
	plug, err := plugin.Open(entryPath)
	if err != nil {
		return err
	}

	// PluginInterface'i ara
	sym, err := plug.Lookup("Plugin")
	if err != nil {
		return err
	}

	pluginInterface, ok := sym.(PluginInterface)
	if !ok {
		return fmt.Errorf("plugin PluginInterface arayüzünü implemente etmiyor")
	}

	p.instance = pluginInterface
	return nil
}

// loadScriptPlugin - Script plugin yükle (placeholder)
func (pm *PluginManager) loadScriptPlugin(p *Plugin) error {
	// JavaScript veya Lua interpreter gerektirir
	// Bu versiyonda sadece placeholder
	return nil
}

// loadExecPlugin - Executable plugin yükle
func (pm *PluginManager) loadExecPlugin(p *Plugin) error {
	entryPath := filepath.Join(p.Path, p.EntryPoint)
	if _, err := os.Stat(entryPath); os.IsNotExist(err) {
		return fmt.Errorf("plugin executable bulunamadı: %s", entryPath)
	}
	return nil
}

// loadWASMPlugin - WASM plugin yükle (placeholder)
func (pm *PluginManager) loadWASMPlugin(p *Plugin) error {
	// WASM runtime gerektirir
	return nil
}

// UnloadPlugin - Plugin kaldır
func (pm *PluginManager) UnloadPlugin(pluginID string) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()

	p, exists := pm.plugins[pluginID]
	if !exists {
		return fmt.Errorf("plugin bulunamadı: %s", pluginID)
	}

	// Çalışıyorsa durdur
	if p.Status == PluginStatusRunning {
		pm.stopPluginInternal(p)
	}

	// Plugin config kaydet
	pm.savePluginConfig(p)

	delete(pm.plugins, pluginID)
	log.Printf("Plugin kaldırıldı: %s", p.Name)
	pm.emitEvent("plugin:unloaded", p)

	return nil
}

// StartPlugin - Plugin başlat
func (pm *PluginManager) StartPlugin(pluginID string) error {
	pm.mu.Lock()
	p, exists := pm.plugins[pluginID]
	pm.mu.Unlock()

	if !exists {
		return fmt.Errorf("plugin bulunamadı: %s", pluginID)
	}

	if p.Status == PluginStatusRunning {
		return nil
	}

	switch p.Type {
	case PluginTypeNative:
		if pi, ok := p.instance.(PluginInterface); ok {
			if err := pi.Init(p.Config); err != nil {
				return err
			}
			if err := pi.Start(); err != nil {
				return err
			}
		}
	case PluginTypeExec:
		return pm.startExecPlugin(p)
	}

	p.Status = PluginStatusRunning
	log.Printf("Plugin başlatıldı: %s", p.Name)
	pm.emitEvent("plugin:started", p)

	return nil
}

// startExecPlugin - Executable plugin başlat
func (pm *PluginManager) startExecPlugin(p *Plugin) error {
	entryPath := filepath.Join(p.Path, p.EntryPoint)

	cmd := exec.Command(entryPath)
	cmd.Dir = p.Path

	// Stdin/Stdout pipe'ları kurulabilir (IPC için)

	if err := cmd.Start(); err != nil {
		return err
	}

	p.process = cmd

	// Process'i izle
	go func() {
		select {
		case <-p.stopChan:
			cmd.Process.Kill()
		}
		cmd.Wait()
	}()

	return nil
}

// StopPlugin - Plugin durdur
func (pm *PluginManager) StopPlugin(pluginID string) error {
	pm.mu.Lock()
	p, exists := pm.plugins[pluginID]
	pm.mu.Unlock()

	if !exists {
		return fmt.Errorf("plugin bulunamadı: %s", pluginID)
	}

	return pm.stopPluginInternal(p)
}

// stopPluginInternal - Plugin iç durdurma
func (pm *PluginManager) stopPluginInternal(p *Plugin) error {
	if p.Status != PluginStatusRunning {
		return nil
	}

	switch p.Type {
	case PluginTypeNative:
		if pi, ok := p.instance.(PluginInterface); ok {
			pi.Stop()
		}
	case PluginTypeExec:
		close(p.stopChan)
		p.stopChan = make(chan struct{})
	}

	p.Status = PluginStatusStopped
	log.Printf("Plugin durduruldu: %s", p.Name)
	pm.emitEvent("plugin:stopped", p)

	return nil
}

// GetPlugins - Tüm pluginleri listele
func (pm *PluginManager) GetPlugins() []*Plugin {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	plugins := make([]*Plugin, 0, len(pm.plugins))
	for _, p := range pm.plugins {
		plugins = append(plugins, p)
	}
	return plugins
}

// GetPlugin - Tek plugin al
func (pm *PluginManager) GetPlugin(pluginID string) *Plugin {
	pm.mu.RLock()
	defer pm.mu.RUnlock()
	return pm.plugins[pluginID]
}

// SendEventToPlugin - Plugin'e event gönder
func (pm *PluginManager) SendEventToPlugin(pluginID, event string, data interface{}) interface{} {
	pm.mu.RLock()
	p, exists := pm.plugins[pluginID]
	pm.mu.RUnlock()

	if !exists || p.Status != PluginStatusRunning {
		return nil
	}

	if p.Type == PluginTypeNative {
		if pi, ok := p.instance.(PluginInterface); ok {
			return pi.HandleEvent(event, data)
		}
	}

	return nil
}

// BroadcastEvent - Tüm pluginlere event gönder
func (pm *PluginManager) BroadcastEvent(event string, data interface{}) {
	pm.mu.RLock()
	plugins := make([]*Plugin, 0, len(pm.plugins))
	for _, p := range pm.plugins {
		plugins = append(plugins, p)
	}
	pm.mu.RUnlock()

	for _, p := range plugins {
		if p.Status == PluginStatusRunning {
			pm.SendEventToPlugin(p.ID, event, data)
		}
	}
}

// RegisterEventHandler - Event handler kaydet
func (pm *PluginManager) RegisterEventHandler(event string, handler PluginEventHandler) {
	pm.mu.Lock()
	defer pm.mu.Unlock()
	pm.eventHandlers[event] = append(pm.eventHandlers[event], handler)
}

// emitEvent - Event yayınla
func (pm *PluginManager) emitEvent(event string, data interface{}) {
	if handlers, ok := pm.eventHandlers[event]; ok {
		for _, handler := range handlers {
			go handler(event, data)
		}
	}
}

// InstallPlugin - Plugin kur (zip'ten)
func (pm *PluginManager) InstallPlugin(zipPath string) error {
	// ZIP dosyasını aç ve plugin dizinine çıkart
	// Manifest kontrolü yap
	// Bütünlük doğrulaması yap
	return nil
}

// UninstallPlugin - Plugin sil
func (pm *PluginManager) UninstallPlugin(pluginID string) error {
	// Önce kaldır
	pm.UnloadPlugin(pluginID)

	// Dosyaları sil
	pluginPath := filepath.Join(pm.pluginDir, pluginID)
	return os.RemoveAll(pluginPath)
}

// UpdatePlugin - Plugin güncelle
func (pm *PluginManager) UpdatePlugin(pluginID string, newZipPath string) error {
	// Eski versiyonu yedekle
	// Yeni versiyonu kur
	// Başarısız olursa geri al
	return nil
}

// checkDependencies - Bağımlılıkları kontrol et
func (pm *PluginManager) checkDependencies(deps []PluginDep) error {
	for _, dep := range deps {
		if _, exists := pm.plugins[dep.ID]; !exists {
			return fmt.Errorf("eksik bağımlılık: %s", dep.ID)
		}
	}
	return nil
}

// isPluginAllowed - Plugin izinli mi
func (pm *PluginManager) isPluginAllowed(pluginID string) bool {
	// Kara liste kontrolü
	for _, blocked := range pm.blockedPlugins {
		if blocked == pluginID {
			return false
		}
	}

	// Beyaz liste varsa kontrol et
	if len(pm.allowedPlugins) > 0 {
		for _, allowed := range pm.allowedPlugins {
			if allowed == pluginID {
				return true
			}
		}
		return false
	}

	return true
}

// calculatePluginHash - Plugin hash hesapla
func (pm *PluginManager) calculatePluginHash(pluginPath string) (string, error) {
	hash := sha256.New()

	err := filepath.Walk(pluginPath, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return err
		}

		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		if _, err := io.Copy(hash, file); err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		return "", err
	}

	return hex.EncodeToString(hash.Sum(nil)), nil
}

// loadPluginConfig - Plugin config yükle
func (pm *PluginManager) loadPluginConfig(p *Plugin) {
	configPath := filepath.Join(pm.configDir, p.ID+".json")
	data, err := os.ReadFile(configPath)
	if err != nil {
		return
	}

	var config map[string]interface{}
	if err := json.Unmarshal(data, &config); err != nil {
		return
	}

	// Manifest config ile birleştir
	for k, v := range config {
		p.Config[k] = v
	}
}

// savePluginConfig - Plugin config kaydet
func (pm *PluginManager) savePluginConfig(p *Plugin) error {
	configPath := filepath.Join(pm.configDir, p.ID+".json")
	data, err := json.MarshalIndent(p.Config, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(configPath, data, 0644)
}

// SetPluginConfig - Plugin config ayarla
func (pm *PluginManager) SetPluginConfig(pluginID string, key string, value interface{}) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()

	p, exists := pm.plugins[pluginID]
	if !exists {
		return fmt.Errorf("plugin bulunamadı: %s", pluginID)
	}

	p.Config[key] = value
	return pm.savePluginConfig(p)
}

// GetPluginConfig - Plugin config al
func (pm *PluginManager) GetPluginConfig(pluginID string, key string) (interface{}, bool) {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	p, exists := pm.plugins[pluginID]
	if !exists {
		return nil, false
	}

	value, ok := p.Config[key]
	return value, ok
}

// EnablePlugin - Plugin etkinleştir
func (pm *PluginManager) EnablePlugin(pluginID string) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()

	p, exists := pm.plugins[pluginID]
	if !exists {
		return fmt.Errorf("plugin bulunamadı: %s", pluginID)
	}

	if p.Status == PluginStatusDisabled {
		p.Status = PluginStatusLoaded
	}

	return nil
}

// DisablePlugin - Plugin devre dışı bırak
func (pm *PluginManager) DisablePlugin(pluginID string) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()

	p, exists := pm.plugins[pluginID]
	if !exists {
		return fmt.Errorf("plugin bulunamadı: %s", pluginID)
	}

	if p.Status == PluginStatusRunning {
		pm.stopPluginInternal(p)
	}

	p.Status = PluginStatusDisabled
	return nil
}

// GetPluginStats - Plugin istatistikleri
func (pm *PluginManager) GetPluginStats() map[string]interface{} {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	var loaded, running, stopped, disabled, error int

	for _, p := range pm.plugins {
		switch p.Status {
		case PluginStatusLoaded:
			loaded++
		case PluginStatusRunning:
			running++
		case PluginStatusStopped:
			stopped++
		case PluginStatusDisabled:
			disabled++
		case PluginStatusError:
			error++
		}
	}

	return map[string]interface{}{
		"total":    len(pm.plugins),
		"loaded":   loaded,
		"running":  running,
		"stopped":  stopped,
		"disabled": disabled,
		"error":    error,
	}
}
