package admin

import (
	"bytes"
	"context"
	"fmt"
	"os/exec"
	"sync"
	"time"
)

type Builder struct {
	store    *Store
	exporter *Exporter
	siteDir  string
	queue    chan string
	once     sync.Once
}

func NewBuilder(store *Store, exporter *Exporter, siteDir string) *Builder {
	return &Builder{
		store:    store,
		exporter: exporter,
		siteDir:  siteDir,
		queue:    make(chan string, 32),
	}
}

func (b *Builder) Start(ctx context.Context) {
	b.once.Do(func() {
		go b.loop(ctx)
	})
}

func (b *Builder) Enqueue(jobID string) bool {
	select {
	case b.queue <- jobID:
		return true
	default:
		return false
	}
}

func (b *Builder) loop(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case jobID := <-b.queue:
			b.runBuildJob(ctx, jobID)
		}
	}
}

func (b *Builder) runBuildJob(ctx context.Context, jobID string) {
	log, err := b.run(ctx, jobID)
	if err != nil {
		_ = b.store.MarkBuildJobFailed(context.Background(), jobID, log, err.Error())
		return
	}
	_ = b.store.MarkBuildJobSuccess(context.Background(), jobID, log)
}

func (b *Builder) run(ctx context.Context, jobID string) (string, error) {
	if err := b.store.MarkBuildJobRunning(ctx, jobID); err != nil {
		return "", err
	}

	posts, err := b.store.PublishedPostsForExport(ctx)
	if err != nil {
		return "", err
	}
	if err := b.exporter.ExportPublishedPosts(ctx, posts); err != nil {
		return "", err
	}

	buildCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
	defer cancel()

	cmd := exec.CommandContext(buildCtx, "npm", "run", "build")
	cmd.Dir = b.siteDir

	var output bytes.Buffer
	cmd.Stdout = &output
	cmd.Stderr = &output
	if err := cmd.Run(); err != nil {
		return output.String(), fmt.Errorf("astro build failed: %w", err)
	}

	return output.String(), nil
}
