package idlewatcher

import (
	"errors"
	"time"

	"github.com/yusing/go-proxy/internal/gperr"
	idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
	"github.com/yusing/go-proxy/internal/task"
	"github.com/yusing/go-proxy/internal/watcher/health"
)

// Start implements health.HealthMonitor.
func (w *Watcher) Start(parent task.Parent) gperr.Error {
	w.task.OnCancel("route_cleanup", func() {
		parent.Finish(w.task.FinishCause())
	})
	return nil
}

// Task implements health.HealthMonitor.
func (w *Watcher) Task() *task.Task {
	return w.task
}

// Finish implements health.HealthMonitor.
func (w *Watcher) Finish(reason any) {
	if w.stream != nil {
		w.stream.Close()
	}
}

// Name implements health.HealthMonitor.
func (w *Watcher) Name() string {
	return w.cfg.ContainerName()
}

// String implements health.HealthMonitor.
func (w *Watcher) String() string {
	return w.Name()
}

// Uptime implements health.HealthMonitor.
func (w *Watcher) Uptime() time.Duration {
	return 0
}

// Latency implements health.HealthMonitor.
func (w *Watcher) Latency() time.Duration {
	return 0
}

// Status implements health.HealthMonitor.
func (w *Watcher) Status() health.Status {
	state := w.state.Load()
	if state.err != nil {
		return health.StatusError
	}
	if state.ready {
		return health.StatusHealthy
	}
	if state.status == idlewatcher.ContainerStatusRunning {
		return health.StatusStarting
	}
	return health.StatusNapping
}

// Detail implements health.HealthMonitor.
func (w *Watcher) Detail() string {
	state := w.state.Load()
	if state.err != nil {
		return state.err.Error()
	}
	if !state.ready {
		return "not ready"
	}
	if state.status == idlewatcher.ContainerStatusRunning {
		return "starting"
	}
	return "napping"
}

func checkUpdateState(key string) (w *Watcher, ready bool, err error) {
	watcherMapMu.RLock()
	w, ok := watcherMap[key]
	if !ok {
		watcherMapMu.RUnlock()
		return nil, false, errors.New("watcher not found")
	}
	watcherMapMu.RUnlock()

	// already ready
	if w.ready() {
		return w, true, nil
	}

	if !w.running() {
		return w, false, nil
	}

	// the new container info not yet updated
	if w.hc.URL().Host == "" {
		return w, false, nil
	}

	res, err := w.hc.CheckHealth()
	if err != nil {
		w.setError(err)
		return w, false, err
	}

	if res.Healthy {
		w.setReady()
		return w, true, nil
	}
	w.setStarting()
	return w, false, nil
}

// MarshalJSON implements health.HealthMonitor.
func (w *Watcher) MarshalJSON() ([]byte, error) {
	url := w.hc.URL()
	if url.Port() == "0" {
		url = nil
	}
	var detail string
	if err := w.error(); err != nil {
		detail = err.Error()
	}
	return (&health.JSONRepresentation{
		Name:   w.Name(),
		Status: w.Status(),
		Config: dummyHealthCheckConfig,
		URL:    url,
		Detail: detail,
	}).MarshalJSON()
}
