package kb

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/service/automation/runtime"
	"github.com/tidbcloud/AgentTeam/internal/store/resource/repos"
)

type KBWikiCommandRunner func(context.Context, string, ...string) error

// KBWikiRuntimeGeneratorConfig composes the existing immutable AGS checkout,
// bounded tree/wiki publishers, and agentteam-insight executable. The generator is
// disabled unless every required production dependency is configured.
type KBWikiRuntimeGeneratorConfig struct {
	Enabled              bool
	Binary               string
	WorkDir              string
	Timeout              time.Duration
	CheckoutMaterializer runtime.CheckoutMaterializer
	WikiPublisher        runtime.WikiPublisher
	TreePublisher        runtime.TreePublisher
	Repositories         *repos.Store
	Command              KBWikiCommandRunner
}

type kbWikiRuntimeGenerator struct{ cfg KBWikiRuntimeGeneratorConfig }

func NewKBWikiRuntimeGenerator(cfg KBWikiRuntimeGeneratorConfig) KBWikiGenerator {
	if !cfg.Enabled || cfg.CheckoutMaterializer == nil || cfg.WikiPublisher == nil || cfg.TreePublisher == nil || cfg.Repositories == nil {
		return nil
	}
	if strings.TrimSpace(cfg.Binary) == "" {
		cfg.Binary = "agentteam-insight"
	}
	if strings.TrimSpace(cfg.WorkDir) == "" {
		cfg.WorkDir = ".data/meshx/kb-wiki-generation"
	}
	if cfg.Timeout <= 0 {
		cfg.Timeout = 2 * time.Hour
	}
	if cfg.Command == nil {
		cfg.Command = func(ctx context.Context, binary string, args ...string) error {
			command := exec.CommandContext(ctx, binary, args...)
			command.Stdout = io.Discard
			command.Stderr = io.Discard
			if err := command.Run(); err != nil {
				return fmt.Errorf("Wiki generator command failed: %w", err)
			}
			return nil
		}
	}
	return &kbWikiRuntimeGenerator{cfg: cfg}
}

func (generator *kbWikiRuntimeGenerator) Generate(ctx context.Context, input KBWikiGenerationInput) (KBWikiGenerationOutput, error) {
	runCtx, cancel := context.WithTimeout(ctx, generator.cfg.Timeout)
	defer cancel()
	repositories, err := generator.cfg.Repositories.ListForResource(runCtx, input.WorkspaceID, repos.ResourceTypeKBSource, input.SourceID)
	if err != nil {
		return KBWikiGenerationOutput{}, fmt.Errorf("load Wiki repositories: %w", err)
	}
	var mirror, insights repos.ResourceRepository
	for _, repo := range repositories {
		switch repo.RepoRole {
		case repos.RepoRoleMirror:
			mirror = repo
		case repos.RepoRoleInsights:
			insights = repo
		}
	}
	if mirror.RepoFullName == "" || insights.RepoFullName == "" {
		return KBWikiGenerationOutput{}, fmt.Errorf("Wiki mirror and insights repository mappings are required")
	}

	root := filepath.Join(generator.cfg.WorkDir, input.RequestID)
	defer os.RemoveAll(root)
	if err := os.RemoveAll(root); err != nil {
		return KBWikiGenerationOutput{}, fmt.Errorf("reset Wiki generation workspace: %w", err)
	}
	if err := os.MkdirAll(root, 0o700); err != nil {
		return KBWikiGenerationOutput{}, fmt.Errorf("create Wiki generation workspace: %w", err)
	}
	sourceDir := filepath.Join(root, "source")
	analysisDir := filepath.Join(root, "analysis")
	wikiDir := filepath.Join(root, "wiki")
	if _, err := generator.cfg.CheckoutMaterializer.Materialize(runCtx, runtime.CheckoutRequest{
		WorkspaceID: input.WorkspaceID, RunID: input.RequestID, JobRunID: input.RequestID,
		RepositoryFullName: mirror.RepoFullName, CommitSHA: input.SourceCommitSHA,
		Destination: sourceDir, Clean: true,
	}); err != nil {
		return KBWikiGenerationOutput{}, fmt.Errorf("materialize Wiki Source: %w", err)
	}
	graphArgs := []string{"graph", "--input", sourceDir, "--output", analysisDir, "--mode", input.Scope, "--no-prompt"}
	wikiArgs := []string{"wiki", "--input", sourceDir, "--input", analysisDir, "--output", wikiDir}
	if input.Language != "" && input.Language != "auto" {
		graphArgs = append(graphArgs, "--output-language", input.Language)
		wikiArgs = append(wikiArgs, "--output-language", input.Language)
	}
	if err := generator.cfg.Command(runCtx, generator.cfg.Binary, graphArgs...); err != nil {
		return KBWikiGenerationOutput{}, err
	}
	if err := generator.cfg.Command(runCtx, generator.cfg.Binary, wikiArgs...); err != nil {
		return KBWikiGenerationOutput{}, err
	}

	tree, err := generator.cfg.TreePublisher.PublishRepositoryTree(runCtx, runtime.TreePublishRequest{
		WorkspaceID: input.WorkspaceID, RunID: input.RequestID, JobRunID: input.RequestID,
		Repository: insights.RepoFullName, SourcePath: analysisDir,
		Message:        "Generate Source analysis for " + input.SourceCommitSHA,
		Description:    "Source-scoped generated Wiki analysis",
		DefaultBranch:  firstNonEmptyString(insights.DefaultRef, "main"),
		Visibility:     firstNonEmptyString(insights.Visibility, "private"),
		RepositoryKind: "kb_insights",
	})
	if err != nil {
		return KBWikiGenerationOutput{}, fmt.Errorf("publish Wiki analysis: %w", err)
	}
	if strings.TrimSpace(tree.CommitSHA) == "" {
		return KBWikiGenerationOutput{}, fmt.Errorf("publish Wiki analysis: publisher returned no commit")
	}
	wiki, err := generator.cfg.WikiPublisher.PublishWikiPages(runCtx, runtime.WikiPublishRequest{
		WorkspaceID: input.WorkspaceID, RunID: input.RequestID, JobRunID: input.RequestID,
		Repository: insights.RepoFullName, SourcePath: wikiDir,
		Message:     "Generate Source Wiki for " + input.SourceCommitSHA,
		Description: "Source-scoped generated Wiki", Label: "code-wiki",
		DefaultBranch:  firstNonEmptyString(insights.DefaultRef, "main"),
		Visibility:     firstNonEmptyString(insights.Visibility, "private"),
		RepositoryKind: "kb_insights",
	})
	if err != nil {
		return KBWikiGenerationOutput{}, fmt.Errorf("publish generated Wiki: %w", err)
	}
	if strings.TrimSpace(wiki.IndexedCommitSHA) == "" {
		return KBWikiGenerationOutput{}, fmt.Errorf("publish generated Wiki: publisher returned no indexed commit")
	}
	return KBWikiGenerationOutput{
		InsightsCommitSHA: tree.CommitSHA, PublishedWikiCommit: wiki.IndexedCommitSHA,
	}, nil
}
