package server

import (
	"context"
	"fmt"
	"net/http"
	"os"

	"github.com/tidbcloud/AgentTeam/internal/database"
	"gorm.io/gorm"
)

func (s *Server) readinessGate(next http.Handler) http.Handler {
	return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		if request.URL.Path == "/readyz" {
			if err := s.Ready(request.Context()); err != nil {
				http.Error(writer, err.Error(), http.StatusServiceUnavailable)
				return
			}
			writer.Header().Set("Content-Type", "application/json")
			_, _ = writer.Write([]byte(`{"status":"ready"}`))
			return
		}
		if request.URL.Path != "/healthz" && !s.ready.Load() {
			http.Error(writer, "runtime is not ready", http.StatusServiceUnavailable)
			return
		}
		next.ServeHTTP(writer, request)
	})
}

// Ready verifies database, filesystem, handler, and worker readiness.
func (s *Server) Ready(ctx context.Context) (err error) {
	if s == nil || s.Handler() == nil {
		return fmt.Errorf("meshx handler is not configured")
	}
	defer func() {
		if err != nil {
			s.workersMu.Lock()
			s.ready.Store(false)
			s.workersMu.Unlock()
		}
	}()
	s.workersMu.Lock()
	workersStarted := s.workersStarted && !s.workersStopped && !s.retiring
	s.workersMu.Unlock()
	if !workersStarted {
		return fmt.Errorf("meshx workers are not started")
	}
	if err := ctx.Err(); err != nil {
		return err
	}
	s.schemaMu.Lock()
	if s.schemaVerified {
		s.schemaMu.Unlock()
	} else if flight := s.schemaFlight; flight != nil {
		s.schemaMu.Unlock()
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-flight.done:
			if flight.err != nil {
				return fmt.Errorf("meshx schema: %w", flight.err)
			}
		}
	} else {
		flight := &schemaVerificationFlight{done: make(chan struct{})}
		s.schemaFlight = flight
		s.schemaMu.Unlock()
		verify := s.verifySchema
		if verify == nil {
			verify = func(ctx context.Context) error { return database.VerifySchema(s.meshxDB.WithContext(ctx)) }
		}
		err := verify(ctx)
		s.schemaMu.Lock()
		if err == nil {
			s.schemaVerified = true
		}
		flight.err = err
		close(flight.done)
		s.schemaFlight = nil
		s.schemaMu.Unlock()
		if err != nil {
			return fmt.Errorf("meshx schema: %w", err)
		}
	}
	if readiness, ok := s.ags.(agsReadiness); ok {
		if err := readiness.Ready(ctx); err != nil {
			return err
		}
	}
	dbs := []struct {
		name     string
		database *gorm.DB
		optional bool
	}{{"meshx database", s.meshxDB, false}, {"agentteam core verifier database", s.agentteamCoreDB, true}}
	for _, dependency := range dbs {
		if dependency.database == nil {
			if dependency.optional {
				continue
			}
			return fmt.Errorf("%s is not configured", dependency.name)
		}
		sqlDB, err := dependency.database.DB()
		if err != nil {
			return fmt.Errorf("%s: %w", dependency.name, err)
		}
		if err := sqlDB.PingContext(ctx); err != nil {
			return fmt.Errorf("%s: %w", dependency.name, err)
		}
	}
	for name, path := range map[string]string{
		"AGS repository root": s.cfg.AGS.GitRepoDir, "automation blob root": s.cfg.Automation.BlobFilesystemRoot,
		"KB source mirror work root": s.cfg.KB.SourceMirrorWorkDir,
	} {
		if _, err := os.Stat(path); err != nil {
			return fmt.Errorf("%s: %w", name, err)
		}
	}
	s.workersMu.Lock()
	if !s.workersStarted || s.workersStopped || s.retiring {
		s.ready.Store(false)
		s.workersMu.Unlock()
		return fmt.Errorf("meshx workers are not running")
	}
	s.ready.Store(true)
	s.workersMu.Unlock()
	return nil
}
