package bridge

import (
	"context"
	"errors"
	"os"
	"os/exec"
	"strings"
	"time"
)

type ServiceStatus struct {
	Unit        string `json:"unit"`
	SocketPath  string `json:"socketPath"`
	SocketReady bool   `json:"socketReady"`
	Active      bool   `json:"active"`
	Enabled     bool   `json:"enabled"`
	Message     string `json:"message"`
}

func (a *App) serviceStatus() ServiceStatus {
	return probeServiceStatus(a.local.Socket, exec.LookPath, runSystemctl, os.Stat)
}

func probeServiceStatus(
	socketPath string,
	lookPath func(string) (string, error),
	run func(args ...string) ([]byte, error),
	stat func(string) (os.FileInfo, error),
) ServiceStatus {
	status := ServiceStatus{
		Unit:       detectServiceUnit(),
		SocketPath: socketPath,
		Message:    "后台服务未启动",
	}

	if _, err := stat(socketPath); err == nil {
		status.SocketReady = true
	}

	if _, err := lookPath("systemctl"); err == nil {
		status.Active = systemctlStateMatches(run, "active", "is-active", status.Unit)
		status.Enabled = systemctlStateMatches(run, "enabled", "is-enabled", status.Unit)
	}

	switch {
	case status.SocketReady:
		status.Message = "后台服务已就绪"
	case status.Active:
		status.Message = "后台服务正在启动"
	default:
		status.Message = "后台服务未启动"
	}

	return status
}

func systemctlStateMatches(run func(args ...string) ([]byte, error), want string, args ...string) bool {
	out, err := run(args...)
	if err != nil {
		return false
	}
	return strings.TrimSpace(string(out)) == want
}

func runSystemctl(args ...string) ([]byte, error) {
	return exec.Command("systemctl", args...).CombinedOutput()
}

func waitForLocalAPIReady(ctx context.Context, timeout, interval time.Duration, probe func(context.Context) error) error {
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	var lastErr error
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		attemptCtx, attemptCancel := context.WithTimeout(ctx, interval)
		err := probe(attemptCtx)
		attemptCancel()
		if err == nil {
			return nil
		}
		lastErr = err

		select {
		case <-ctx.Done():
			if lastErr != nil {
				return lastErr
			}
			if errors.Is(ctx.Err(), context.DeadlineExceeded) {
				return ctx.Err()
			}
			return ctx.Err()
		case <-ticker.C:
		}
	}
}
