package search

import (
	"fmt"
	"regexp"
	"strings"
	"sync"
	"time"
)

// WorkflowEntry represents an indexed workflow
type WorkflowEntry struct {
	ID        string
	Name      string
	Status    string
	CreatedAt time.Time
	UpdatedAt time.Time
	Tags      []string
	Content   string
	Assignee  string
}

// WorkflowSearch provides full-text search and filtering
type WorkflowSearch struct {
	mu       sync.RWMutex
	entries  map[string]*WorkflowEntry
	index    map[string][]string // word -> workflow IDs
	filters  map[string]interface{}
}

// NewWorkflowSearch creates a new workflow search index
func NewWorkflowSearch() *WorkflowSearch {
	return &WorkflowSearch{
		entries:  make(map[string]*WorkflowEntry),
		index:    make(map[string][]string),
		filters:  make(map[string]interface{}),
	}
}

// Index adds a workflow to the search index
func (ws *WorkflowSearch) Index(entry *WorkflowEntry) error {
	if entry.ID == "" {
		return fmt.Errorf("workflow ID required")
	}

	ws.mu.Lock()
	defer ws.mu.Unlock()

	ws.entries[entry.ID] = entry

	// Index content
	words := strings.Fields(strings.ToLower(entry.Content + " " + entry.Name))
	for _, word := range words {
		// Remove punctuation
		clean := strings.Trim(word, ".,!?;:")
		if clean != "" {
			ws.index[clean] = append(ws.index[clean], entry.ID)
		}
	}

	return nil
}

// Search performs full-text search
func (ws *WorkflowSearch) Search(query string) []*WorkflowEntry {
	ws.mu.RLock()
	defer ws.mu.RUnlock()

	query = strings.ToLower(query)
	matches := make(map[string]int)

	words := strings.Fields(query)
	for _, word := range words {
		if ids, exists := ws.index[word]; exists {
			for _, id := range ids {
				matches[id]++
			}
		}
	}

	// Sort by match count
	result := make([]*WorkflowEntry, 0)
	for id := range matches {
		if entry, exists := ws.entries[id]; exists {
			result = append(result, entry)
		}
	}

	return result
}

// FilterByStatus filters workflows by status
func (ws *WorkflowSearch) FilterByStatus(status string) []*WorkflowEntry {
	ws.mu.RLock()
	defer ws.mu.RUnlock()

	result := make([]*WorkflowEntry, 0)
	for _, entry := range ws.entries {
		if entry.Status == status {
			result = append(result, entry)
		}
	}

	return result
}

// FilterByAssignee filters workflows by assignee
func (ws *WorkflowSearch) FilterByAssignee(assignee string) []*WorkflowEntry {
	ws.mu.RLock()
	defer ws.mu.RUnlock()

	result := make([]*WorkflowEntry, 0)
	for _, entry := range ws.entries {
		if entry.Assignee == assignee {
			result = append(result, entry)
		}
	}

	return result
}

// FilterByTag filters workflows by tag
func (ws *WorkflowSearch) FilterByTag(tag string) []*WorkflowEntry {
	ws.mu.RLock()
	defer ws.mu.RUnlock()

	result := make([]*WorkflowEntry, 0)
	for _, entry := range ws.entries {
		for _, t := range entry.Tags {
			if t == tag {
				result = append(result, entry)
				break
			}
		}
	}

	return result
}

// FilterByDateRange filters workflows by date range
func (ws *WorkflowSearch) FilterByDateRange(start, end time.Time) []*WorkflowEntry {
	ws.mu.RLock()
	defer ws.mu.RUnlock()

	result := make([]*WorkflowEntry, 0)
	for _, entry := range ws.entries {
		if entry.CreatedAt.After(start) && entry.CreatedAt.Before(end) {
			result = append(result, entry)
		}
	}

	return result
}

// SearchRegex performs regex search on content
func (ws *WorkflowSearch) SearchRegex(pattern string) ([]*WorkflowEntry, error) {
	re, err := regexp.Compile(pattern)
	if err != nil {
		return nil, err
	}

	ws.mu.RLock()
	defer ws.mu.RUnlock()

	result := make([]*WorkflowEntry, 0)
	for _, entry := range ws.entries {
		if re.MatchString(entry.Content) || re.MatchString(entry.Name) {
			result = append(result, entry)
		}
	}

	return result, nil
}

// SaveFilter saves a named filter
func (ws *WorkflowSearch) SaveFilter(name string, filter interface{}) {
	ws.mu.Lock()
	defer ws.mu.Unlock()

	ws.filters[name] = filter
}

// GetFilter retrieves a saved filter
func (ws *WorkflowSearch) GetFilter(name string) (interface{}, bool) {
	ws.mu.RLock()
	defer ws.mu.RUnlock()

	filter, exists := ws.filters[name]
	return filter, exists
}

// GetAll returns all workflows
func (ws *WorkflowSearch) GetAll() []*WorkflowEntry {
	ws.mu.RLock()
	defer ws.mu.RUnlock()

	result := make([]*WorkflowEntry, 0, len(ws.entries))
	for _, entry := range ws.entries {
		result = append(result, entry)
	}

	return result
}

// GetByID retrieves a workflow by ID
func (ws *WorkflowSearch) GetByID(id string) (*WorkflowEntry, bool) {
	ws.mu.RLock()
	defer ws.mu.RUnlock()

	entry, exists := ws.entries[id]
	return entry, exists
}

// Delete removes a workflow from the index
func (ws *WorkflowSearch) Delete(id string) error {
	ws.mu.Lock()
	defer ws.mu.Unlock()

	if _, exists := ws.entries[id]; !exists {
		return fmt.Errorf("workflow not found: %s", id)
	}

	delete(ws.entries, id)
	return nil
}

// Clear clears the entire index
func (ws *WorkflowSearch) Clear() {
	ws.mu.Lock()
	defer ws.mu.Unlock()

	ws.entries = make(map[string]*WorkflowEntry)
	ws.index = make(map[string][]string)
}
