package pause

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"
)

// WorkflowSnapshot represents a complete snapshot of workflow state
type WorkflowSnapshot struct {
	WorkflowID        string                 `json:"workflow_id"`
	Timestamp         time.Time              `json:"timestamp"`
	Stage             string                 `json:"stage"`
	CompletedTasks    []string               `json:"completed_tasks"`
	PendingTasks      []string               `json:"pending_tasks"`
	FailedTasks       []string               `json:"failed_tasks"`
	CurrentTaskID     string                 `json:"current_task_id"`
	CurrentActivityID string                 `json:"current_activity_id"`
	TaskMetrics       map[string]interface{} `json:"task_metrics"`
	WorkflowMetrics   map[string]interface{} `json:"workflow_metrics"`
	Configuration    map[string]interface{} `json:"configuration"`
	Error             string                 `json:"error,omitempty"`
	PausedAt          time.Time              `json:"paused_at"`
	ResumedAt         *time.Time             `json:"resumed_at,omitempty"`
}

// SnapshotManager manages workflow state snapshots for pause/resume
type SnapshotManager struct {
	mu         sync.RWMutex
	basePath   string
	snapshots  map[string]*WorkflowSnapshot
	lastSnapshot *WorkflowSnapshot
}

// NewSnapshotManager creates a new snapshot manager
func NewSnapshotManager(basePath string) *SnapshotManager {
	return &SnapshotManager{
		basePath:  basePath,
		snapshots: make(map[string]*WorkflowSnapshot),
	}
}

// CreateSnapshot creates and persists a workflow snapshot
func (sm *SnapshotManager) CreateSnapshot(
	workflowID string,
	stage string,
	completedTasks, pendingTasks, failedTasks []string,
	currentTaskID, currentActivityID string,
	taskMetrics, workflowMetrics, configuration map[string]interface{},
) (*WorkflowSnapshot, error) {
	sm.mu.Lock()
	defer sm.mu.Unlock()

	snapshot := &WorkflowSnapshot{
		WorkflowID:       workflowID,
		Timestamp:        time.Now(),
		Stage:            stage,
		CompletedTasks:   completedTasks,
		PendingTasks:     pendingTasks,
		FailedTasks:      failedTasks,
		CurrentTaskID:    currentTaskID,
		CurrentActivityID: currentActivityID,
		TaskMetrics:      taskMetrics,
		WorkflowMetrics:  workflowMetrics,
		Configuration:    configuration,
		PausedAt:         time.Now(),
	}

	sm.snapshots[workflowID] = snapshot
	sm.lastSnapshot = snapshot

	return snapshot, sm.persistLocked(workflowID, snapshot)
}

// GetLatestSnapshot retrieves the latest snapshot for a workflow
func (sm *SnapshotManager) GetLatestSnapshot(workflowID string) *WorkflowSnapshot {
	sm.mu.RLock()
	defer sm.mu.RUnlock()

	return sm.snapshots[workflowID]
}

// HasSnapshot checks if a snapshot exists for a workflow
func (sm *SnapshotManager) HasSnapshot(workflowID string) bool {
	sm.mu.RLock()
	defer sm.mu.RUnlock()

	_, exists := sm.snapshots[workflowID]
	return exists
}

// RestoreFromSnapshot restores workflow state from a snapshot
func (sm *SnapshotManager) RestoreFromSnapshot(workflowID string) (*WorkflowSnapshot, error) {
	sm.mu.Lock()
	defer sm.mu.Unlock()

	snapshot, exists := sm.snapshots[workflowID]
	if !exists {
		// Try to load from disk
		return nil, fmt.Errorf("no snapshot found for workflow: %s", workflowID)
	}

	// Mark as resumed
	now := time.Now()
	snapshot.ResumedAt = &now

	return snapshot, nil
}

// MarkResumed updates a snapshot as resumed
func (sm *SnapshotManager) MarkResumed(workflowID string) error {
	sm.mu.Lock()
	defer sm.mu.Unlock()

	snapshot, exists := sm.snapshots[workflowID]
	if !exists {
		return fmt.Errorf("no snapshot found for workflow: %s", workflowID)
	}

	now := time.Now()
	snapshot.ResumedAt = &now

	return sm.persistLocked(workflowID, snapshot)
}

// Load loads snapshots from disk
func (sm *SnapshotManager) Load() error {
	sm.mu.Lock()
	defer sm.mu.Unlock()

	snapshotDir := filepath.Join(sm.basePath, "snapshots")
	entries, err := os.ReadDir(snapshotDir)
	if err != nil {
		if os.IsNotExist(err) {
			return nil // Directory doesn't exist yet
		}
		return err
	}

	for _, entry := range entries {
		if !entry.IsDir() && filepath.Ext(entry.Name()) == ".json" {
			data, err := os.ReadFile(filepath.Join(snapshotDir, entry.Name()))
			if err != nil {
				continue
			}

			var snapshot WorkflowSnapshot
			if err := json.Unmarshal(data, &snapshot); err != nil {
				continue
			}

			sm.snapshots[snapshot.WorkflowID] = &snapshot
		}
	}

	return nil
}

// persistLocked saves a snapshot to disk (must be called with lock held)
func (sm *SnapshotManager) persistLocked(workflowID string, snapshot *WorkflowSnapshot) error {
	snapshotDir := filepath.Join(sm.basePath, "snapshots")

	// Create directory if it doesn't exist
	if err := os.MkdirAll(snapshotDir, 0755); err != nil {
		return err
	}

	snapshotPath := filepath.Join(snapshotDir, fmt.Sprintf("%s.snapshot.json", workflowID))

	data, err := json.MarshalIndent(snapshot, "", "  ")
	if err != nil {
		return err
	}

	return os.WriteFile(snapshotPath, data, 0644)
}

// DeleteSnapshot deletes a snapshot (after successful completion)
func (sm *SnapshotManager) DeleteSnapshot(workflowID string) error {
	sm.mu.Lock()
	defer sm.mu.Unlock()

	delete(sm.snapshots, workflowID)

	snapshotPath := filepath.Join(sm.basePath, "snapshots", fmt.Sprintf("%s.snapshot.json", workflowID))
	if _, err := os.Stat(snapshotPath); err == nil {
		return os.Remove(snapshotPath)
	}

	return nil
}

// GetAllSnapshots returns all snapshots
func (sm *SnapshotManager) GetAllSnapshots() []*WorkflowSnapshot {
	sm.mu.RLock()
	defer sm.mu.RUnlock()

	snapshots := make([]*WorkflowSnapshot, 0, len(sm.snapshots))
	for _, snapshot := range sm.snapshots {
		snapshots = append(snapshots, snapshot)
	}

	return snapshots
}

// GetLastSnapshot returns the last snapshot created
func (sm *SnapshotManager) GetLastSnapshot() *WorkflowSnapshot {
	sm.mu.RLock()
	defer sm.mu.RUnlock()

	return sm.lastSnapshot
}

// GetSnapshotStats returns statistics about snapshots
func (sm *SnapshotManager) GetSnapshotStats() map[string]interface{} {
	sm.mu.RLock()
	defer sm.mu.RUnlock()

	paused := 0
	resumed := 0

	for _, snapshot := range sm.snapshots {
		if snapshot.ResumedAt != nil {
			resumed++
		} else {
			paused++
		}
	}

	return map[string]interface{}{
		"total":   len(sm.snapshots),
		"paused":  paused,
		"resumed": resumed,
	}
}

// ClearOldSnapshots removes snapshots older than the specified duration
func (sm *SnapshotManager) ClearOldSnapshots(maxAge time.Duration) (int, error) {
	sm.mu.Lock()
	defer sm.mu.Unlock()

	now := time.Now()
	toDelete := make([]string, 0)

	for wfID, snapshot := range sm.snapshots {
		if now.Sub(snapshot.PausedAt) > maxAge {
			toDelete = append(toDelete, wfID)
		}
	}

	for _, wfID := range toDelete {
		delete(sm.snapshots, wfID)
		snapshotPath := filepath.Join(sm.basePath, "snapshots", fmt.Sprintf("%s.snapshot.json", wfID))
		_ = os.Remove(snapshotPath)
	}

	return len(toDelete), nil
}
