package services

import (
	"sync"
	"time"

	apperrors "github.com/darksworm/argonaut/pkg/errors"
	"github.com/darksworm/argonaut/pkg/logging"
	"github.com/darksworm/argonaut/pkg/model"
)

// DegradationMode represents different levels of service degradation
type DegradationMode string

const (
	DegradationNone     DegradationMode = "none"     // Full functionality
	DegradationPartial  DegradationMode = "partial"  // Some features disabled
	DegradationOffline  DegradationMode = "offline"  // Offline/cached mode
	DegradationReadOnly DegradationMode = "readonly" // Read-only operations
)

// ServiceHealth represents the health status of various services
type ServiceHealth struct {
	ArgoAPI        HealthStatus    `json:"argoAPI"`
	Authentication HealthStatus    `json:"authentication"`
	Connectivity   HealthStatus    `json:"connectivity"`
	LastCheck      time.Time       `json:"lastCheck"`
	Mode           DegradationMode `json:"mode"`
}

// HealthStatus represents the status of a service component
type HealthStatus struct {
	Status   string    `json:"status"` // healthy, degraded, unavailable
	LastSeen time.Time `json:"lastSeen"`
	Failures int       `json:"failures"`
	Message  string    `json:"message,omitempty"`
}

// GracefulDegradationManager handles service degradation scenarios
type GracefulDegradationManager struct {
	health            ServiceHealth
	mu                sync.RWMutex
	logger            logging.Logger
	cache             *ServiceCache
	healthCheckTicker *time.Ticker
	shutdown          chan struct{}
	callbacks         []DegradationCallback
}

// DegradationCallback is called when degradation mode changes
type DegradationCallback func(oldMode, newMode DegradationMode)

// ServiceCache provides cached data for offline mode
type ServiceCache struct {
	Apps        []model.App   `json:"apps"`
	LastUpdated time.Time     `json:"lastUpdated"`
	Server      *model.Server `json:"server"`
	APIVersion  string        `json:"apiVersion"`
}

// NewGracefulDegradationManager creates a new degradation manager
func NewGracefulDegradationManager() *GracefulDegradationManager {
	manager := &GracefulDegradationManager{
		health: ServiceHealth{
			ArgoAPI:        HealthStatus{Status: "unknown", LastSeen: time.Now()},
			Authentication: HealthStatus{Status: "unknown", LastSeen: time.Now()},
			Connectivity:   HealthStatus{Status: "unknown", LastSeen: time.Now()},
			LastCheck:      time.Now(),
			Mode:           DegradationNone,
		},
		logger:   logging.GetDefaultLogger().WithComponent("degradation"),
		cache:    &ServiceCache{},
		shutdown: make(chan struct{}),
	}

	// Start health monitoring
	manager.startHealthMonitoring()

	return manager
}

// RegisterCallback registers a callback for degradation mode changes
func (m *GracefulDegradationManager) RegisterCallback(callback DegradationCallback) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.callbacks = append(m.callbacks, callback)
}

// ReportAPIHealth reports the health status of the ArgoCD API
func (m *GracefulDegradationManager) ReportAPIHealth(healthy bool, err error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	if healthy {
		m.health.ArgoAPI.Status = "healthy"
		m.health.ArgoAPI.LastSeen = time.Now()
		m.health.ArgoAPI.Failures = 0
		m.health.ArgoAPI.Message = ""
	} else {
		m.health.ArgoAPI.Failures++
		m.health.ArgoAPI.Message = ""

		if err != nil {
			if argErr, ok := err.(*apperrors.ArgonautError); ok {
				switch argErr.Category {
				case apperrors.ErrorAuth:
					m.health.Authentication.Status = "unavailable"
					m.health.ArgoAPI.Status = "degraded"
					m.health.ArgoAPI.Message = "Authentication required"
				case apperrors.ErrorNetwork, apperrors.ErrorTimeout:
					m.health.Connectivity.Status = "unavailable"
					m.health.ArgoAPI.Status = "unavailable"
					m.health.ArgoAPI.Message = "Network connectivity issues"
				default:
					m.health.ArgoAPI.Status = "degraded"
					m.health.ArgoAPI.Message = argErr.Message
				}
			} else {
				m.health.ArgoAPI.Status = "unavailable"
				m.health.ArgoAPI.Message = err.Error()
			}
		}
	}

	m.health.LastCheck = time.Now()
	m.updateDegradationMode()
}

// updateDegradationMode determines the appropriate degradation mode based on health
func (m *GracefulDegradationManager) updateDegradationMode() {
	oldMode := m.health.Mode
	var newMode DegradationMode

	// Determine degradation mode based on service health
	if m.health.Authentication.Status == "unavailable" {
		newMode = DegradationOffline
	} else if m.health.Connectivity.Status == "unavailable" {
		newMode = DegradationOffline
	} else if m.health.ArgoAPI.Status == "unavailable" {
		newMode = DegradationOffline
	} else if m.health.ArgoAPI.Status == "degraded" {
		newMode = DegradationPartial
	} else {
		newMode = DegradationNone
	}

	if newMode != oldMode {
		m.health.Mode = newMode
		m.logger.Info("Degradation mode changed: %s -> %s", oldMode, newMode)

		// Notify callbacks
		for _, callback := range m.callbacks {
			go callback(oldMode, newMode)
		}
	}
}

// GetCurrentMode returns the current degradation mode
func (m *GracefulDegradationManager) GetCurrentMode() DegradationMode {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.health.Mode
}

// CanPerformOperation checks if an operation is allowed in current degradation mode
func (m *GracefulDegradationManager) CanPerformOperation(operation string) (bool, *apperrors.ArgonautError) {
	mode := m.GetCurrentMode()

	switch mode {
	case DegradationNone:
		return true, nil

	case DegradationPartial:
		// Allow read operations, restrict write operations
		readOps := []string{"ListApplications", "GetApplication", "GetResourceDiffs", "GetAPIVersion"}
		for _, op := range readOps {
			if op == operation {
				return true, nil
			}
		}
		return false, apperrors.New(apperrors.ErrorUnavailable, "OPERATION_RESTRICTED",
			"Operation restricted due to service degradation").
			WithUserAction("Some features are temporarily unavailable due to service issues")

	case DegradationReadOnly:
		// Only allow read operations
		readOps := []string{"ListApplications", "GetApplication", "GetResourceDiffs", "GetAPIVersion"}
		for _, op := range readOps {
			if op == operation {
				return true, nil
			}
		}
		return false, apperrors.New(apperrors.ErrorUnavailable, "READONLY_MODE",
			"System is in read-only mode").
			WithUserAction("Write operations are disabled. Please try again later")

	case DegradationOffline:
		// Only allow cached operations
		cachedOps := []string{"ListApplications"} // We can serve from cache
		for _, op := range cachedOps {
			if op == operation {
				return true, nil
			}
		}
		return false, apperrors.New(apperrors.ErrorUnavailable, "OFFLINE_MODE",
			"System is offline - limited functionality available").
			WithUserAction("Check your connection and try again. Some cached data may be available")

	default:
		return false, apperrors.New(apperrors.ErrorInternal, "UNKNOWN_MODE",
			"Unknown degradation mode")
	}
}

// startHealthMonitoring starts periodic health checks
func (m *GracefulDegradationManager) startHealthMonitoring() {
	m.healthCheckTicker = time.NewTicker(30 * time.Second)

	go func() {
		for {
			select {
			case <-m.shutdown:
				return
			case <-m.healthCheckTicker.C:
				m.performHealthCheck()
			}
		}
	}()
}

// performHealthCheck performs periodic health validation
func (m *GracefulDegradationManager) performHealthCheck() {
	m.mu.Lock()
	defer m.mu.Unlock()

	now := time.Now()

	// Check if services haven't been seen for too long
	if now.Sub(m.health.ArgoAPI.LastSeen) > 2*time.Minute && m.health.ArgoAPI.Status == "healthy" {
		m.health.ArgoAPI.Status = "degraded"
		m.health.ArgoAPI.Message = "Service hasn't been seen recently"
		m.logger.Warn("ArgoCD API marked as degraded due to inactivity")
	}

	if now.Sub(m.health.Connectivity.LastSeen) > 2*time.Minute && m.health.Connectivity.Status == "healthy" {
		m.health.Connectivity.Status = "degraded"
		m.health.Connectivity.Message = "Connectivity check overdue"
		m.logger.Warn("Connectivity marked as degraded due to inactivity")
	}

	m.health.LastCheck = now
	m.updateDegradationMode()
}
