package webui

import (
	"sync"
	"time"
)

// jobState holds the output lines and completion status of an async job.
type jobState struct {
	lines  []string
	done   bool
	err    string
	mu     sync.Mutex
	subs   []chan string
	cancel func() // optional cancel function; nil if job is not cancellable
}

// abort cancels the job if it has a cancel function and is not yet done.
func (j *jobState) abort() bool {
	j.mu.Lock()
	defer j.mu.Unlock()
	if j.done || j.cancel == nil {
		return false
	}
	j.cancel()
	return true
}

func (j *jobState) append(line string) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.lines = append(j.lines, line)
	for _, ch := range j.subs {
		select {
		case ch <- line:
		default:
		}
	}
}

func (j *jobState) finish(errMsg string) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.done = true
	j.err = errMsg
	for _, ch := range j.subs {
		close(ch)
	}
	j.subs = nil
}

// subscribe returns a channel that receives all future lines.
// Existing lines are returned first, then the channel streams new ones.
func (j *jobState) subscribe() ([]string, <-chan string) {
	j.mu.Lock()
	defer j.mu.Unlock()
	existing := make([]string, len(j.lines))
	copy(existing, j.lines)
	if j.done {
		return existing, nil
	}
	ch := make(chan string, 256)
	j.subs = append(j.subs, ch)
	return existing, ch
}

// jobManager manages async jobs identified by string IDs.
type jobManager struct {
	mu   sync.Mutex
	jobs map[string]*jobState
}

var globalJobs = &jobManager{jobs: make(map[string]*jobState)}

func (m *jobManager) create(id string) *jobState {
	m.mu.Lock()
	defer m.mu.Unlock()
	j := &jobState{}
	m.jobs[id] = j
	// Schedule cleanup after 30 minutes
	go func() {
		time.Sleep(30 * time.Minute)
		m.mu.Lock()
		delete(m.jobs, id)
		m.mu.Unlock()
	}()
	return j
}

// isDone returns true if the job has finished (either successfully or with error).
func (j *jobState) isDone() bool {
	j.mu.Lock()
	defer j.mu.Unlock()
	return j.done
}

func (m *jobManager) get(id string) (*jobState, bool) {
	m.mu.Lock()
	defer m.mu.Unlock()
	j, ok := m.jobs[id]
	return j, ok
}
