package app

import (
	"context"
	"fmt"
	"log/slog"
	"os"

	"github.com/dtoro/oikos/internal/core/ports"
)

// SeedService is the bootstrap/DR use-case: ingest the seeds/*.yaml files
// into the DB (idempotent, ontology-validated) and regenerate them from
// the DB for version control. Absorbed the ingest orchestration that
// lived in cmd/oikos runSeed and the export entry point in runExport
// (Phase 7); the SQL stays behind ports.SeedRepository in the postgres
// adapter.
type SeedService struct {
	repo ports.SeedRepository
}

// NewSeedService wires the service.
func NewSeedService(repo ports.SeedRepository) *SeedService {
	return &SeedService{repo: repo}
}

// Ingest reads the seed YAMLs from dir and ingests them in dependency
// order: ontology (types) before inventory (entities/edges validated
// against them), then policy, then knowledge — which is optional and
// skipped silently when absent. Files whose content hash is unchanged
// are no-ops at the repository layer.
func (s *SeedService) Ingest(ctx context.Context, dir string) error {
	type step struct {
		file    string
		ingest  func(ctx context.Context, filename string, content []byte) (ports.SeedCounts, bool, error)
		optional bool
	}
	steps := []step{
		{"ontology.yaml", s.repo.IngestOntology, false},
		{"inventory.yaml", s.repo.IngestInventory, false},
		{"policy.yaml", s.repo.IngestPolicy, false},
		{"knowledge.yaml", s.repo.IngestKnowledge, true},
	}

	for _, st := range steps {
		content, err := os.ReadFile(dir + "/" + st.file)
		if err != nil {
			if os.IsNotExist(err) && st.optional {
				slog.Info("knowledge seed not found, skipping")
				continue
			}
			return fmt.Errorf("read %s seed: %w", st.file, err)
		}

		counts, applied, err := st.ingest(ctx, st.file, content)
		if err != nil {
			return err
		}
		if !applied {
			continue
		}

		switch st.file {
		case "ontology.yaml":
			slog.Info("ontology ingested",
				"lifecycles", counts.Lifecycles,
				"entity_types", counts.EntityTypes,
				"relationship_types", counts.RelationshipTypes)
		case "inventory.yaml":
			slog.Info("inventory ingested",
				"entities", counts.Entities,
				"relationships", counts.Relationships)
		case "policy.yaml":
			slog.Info("policy ingested",
				"risk_classes", counts.RiskClasses,
				"approval_rules", counts.ApprovalRules,
				"autonomy_settings", counts.AutonomySettings)
		case "knowledge.yaml":
			slog.Info("knowledge ingested",
				"documents", counts.Documents,
				"investigations", counts.Investigations,
				"runbooks", counts.Runbooks)
		}
	}

	slog.Info("seed ingest complete")
	return nil
}

// Export regenerates the seed YAMLs from the DB (DR / version control).
// Deterministic: the repository orders lists and sorts map keys so
// export → ingest → export is byte-stable.
func (s *SeedService) Export(ctx context.Context) (map[string][]byte, error) {
	return s.repo.Export(ctx)
}
