package jobdef

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/redis/go-redis/v9"
	"go.mongodb.org/mongo-driver/mongo"

	"scheduler-backend/pkg/config"
	"scheduler-backend/pkg/log"
)

const logTailLines = 2000

type LogCollector struct {
	mu    sync.Mutex
	lines []string
}

func NewLogCollector() *LogCollector {
	return &LogCollector{}
}

func (c *LogCollector) Appendf(format string, args ...any) {
	if c == nil {
		return
	}
	c.mu.Lock()
	c.lines = append(c.lines, fmt.Sprintf(format, args...))
	c.mu.Unlock()
}

func (c *LogCollector) String() string {
	if c == nil {
		return ""
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	return strings.Join(c.lines, "\n")
}

type FlushResult struct {
	LogText string
	LogFile string
}

func (c *LogCollector) Flush(executionID string) FlushResult {
	if c == nil {
		return FlushResult{}
	}
	c.mu.Lock()
	lines := c.lines
	c.mu.Unlock()

	if len(lines) == 0 {
		return FlushResult{}
	}

	if len(lines) <= logTailLines {
		return FlushResult{LogText: strings.Join(lines, "\n")}
	}

	filename := executionID + ".log"
	dir := filepath.Join("logs", "joblog")
	_ = os.MkdirAll(dir, 0o755)
	fullPath := filepath.Join(dir, filename)
	_ = os.WriteFile(fullPath, []byte(strings.Join(lines, "\n")+"\n"), 0o644)

	tail := lines[len(lines)-logTailLines:]
	header := fmt.Sprintf("[完整日志: %s (%d行)]", filename, len(lines))

	return FlushResult{
		LogText: header + "\n" + strings.Join(tail, "\n"),
		LogFile: filename,
	}
}

type ExecuteRequest struct {
	ExecutionID  string          `json:"executionID"`
	JobID        string          `json:"jobID"`
	TriggerType  string          `json:"triggerType"`
	Params       json.RawMessage `json:"params"`
	LogCollector *LogCollector   `json:"-"`
}

type Runtime struct {
	Config     config.Config
	Logger     log.Logger
	MetaDB     *mongo.Database
	BusinessDB *mongo.Database
	Redis      *redis.Client
}

type Handler interface {
	Key() string
	Name() string
	Description() string
	Run(ctx context.Context, runtime Runtime, req ExecuteRequest) error
}
