// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v2

import (
	"encoding/json"
	"net/http"
	"path"
	"sort"
	"strings"

	issues_model "code.gitea.io/gitea/models/issues"
	repo_model "code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/models/unit"
	apierrors "code.gitea.io/gitea/modules/errors"
	"code.gitea.io/gitea/modules/git"
	"code.gitea.io/gitea/modules/gitrepo"
	"code.gitea.io/gitea/services/context"
)

// AIRepoSummaryRequest represents a request for AI-friendly repo summary
type AIRepoSummaryRequest struct {
	Owner string `json:"owner" binding:"Required"`
	Repo  string `json:"repo" binding:"Required"`
	Ref   string `json:"ref"`
}

// AIRepoSummaryResponse contains comprehensive repo information for AI tools
type AIRepoSummaryResponse struct {
	// Basic info
	Owner       string `json:"owner"`
	Repo        string `json:"repo"`
	FullName    string `json:"full_name"`
	Description string `json:"description"`
	Website     string `json:"website,omitempty"`
	Language    string `json:"primary_language"`

	// Repository stats
	Stars       int   `json:"stars"`
	Forks       int   `json:"forks"`
	Watchers    int   `json:"watchers"`
	OpenIssues  int   `json:"open_issues"`
	OpenPRs     int   `json:"open_pull_requests"`
	Size        int64 `json:"size_kb"`
	IsPrivate   bool  `json:"is_private"`
	IsFork      bool  `json:"is_fork"`
	IsArchived  bool  `json:"is_archived"`
	IsTemplate  bool  `json:"is_template"`
	HasWiki     bool  `json:"has_wiki"`
	HasIssues   bool  `json:"has_issues"`
	HasProjects bool  `json:"has_projects"`

	// Git info
	DefaultBranch string   `json:"default_branch"`
	Branches      []string `json:"branches"`
	Tags          []string `json:"recent_tags"`
	LastCommit    struct {
		SHA       string `json:"sha"`
		Message   string `json:"message"`
		Author    string `json:"author"`
		Timestamp string `json:"timestamp"`
	} `json:"last_commit"`

	// Structure overview
	Structure struct {
		TopLevelDirs  []string          `json:"top_level_dirs"`
		TopLevelFiles []string          `json:"top_level_files"`
		FileCount     int               `json:"total_file_count"`
		Languages     map[string]int64  `json:"languages"` // language -> bytes
		HasReadme     bool              `json:"has_readme"`
		ReadmePath    string            `json:"readme_path,omitempty"`
		HasLicense    bool              `json:"has_license"`
		LicensePath   string            `json:"license_path,omitempty"`
		HasContrib    bool              `json:"has_contributing"`
		ContribPath   string            `json:"contributing_path,omitempty"`
		ConfigFiles   []string          `json:"config_files"` // package.json, go.mod, etc.
	} `json:"structure"`

	// Recent activity
	RecentActivity struct {
		CommitsLastWeek  int `json:"commits_last_week"`
		CommitsLastMonth int `json:"commits_last_month"`
		Contributors     int `json:"contributors"`
	} `json:"recent_activity"`

	// AI-specific hints
	AIHints struct {
		SuggestedEntryPoints []string `json:"suggested_entry_points"`
		ProjectType          string   `json:"project_type"` // "library", "application", "monorepo", etc.
		BuildSystem          string   `json:"build_system,omitempty"`
		TestFramework        string   `json:"test_framework,omitempty"`
	} `json:"ai_hints"`
}

// GetAIRepoSummary returns a comprehensive AI-friendly summary of a repository
func GetAIRepoSummary(ctx *context.APIContext) {
	var req AIRepoSummaryRequest
	if err := json.NewDecoder(ctx.Req.Body).Decode(&req); err != nil {
		ctx.APIErrorWithCode(apierrors.ValidationFailed, map[string]any{
			"error": err.Error(),
		})
		return
	}

	// Get repository
	repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, req.Owner, req.Repo)
	if err != nil {
		if repo_model.IsErrRepoNotExist(err) {
			ctx.APIErrorWithCode(apierrors.RepoNotFound)
			return
		}
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}

	// Check access
	if repo.IsPrivate && !ctx.IsSigned {
		ctx.APIErrorWithCode(apierrors.PermAccessDenied)
		return
	}

	// Load owner
	if err := repo.LoadOwner(ctx); err != nil {
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}

	// Open git repo
	gitRepo, err := gitrepo.OpenRepository(ctx, repo)
	if err != nil {
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}
	defer gitRepo.Close()

	response := AIRepoSummaryResponse{
		Owner:         repo.Owner.Name,
		Repo:          repo.Name,
		FullName:      repo.FullName(),
		Description:   repo.Description,
		Website:       repo.Website,
		Language:      repo.PrimaryLanguage.Language,
		Stars:         repo.NumStars,
		Forks:         repo.NumForks,
		Watchers:      repo.NumWatches,
		OpenIssues:    repo.NumOpenIssues,
		OpenPRs:       repo.NumOpenPulls,
		Size:          repo.Size,
		IsPrivate:     repo.IsPrivate,
		IsFork:        repo.IsFork,
		IsArchived:    repo.IsArchived,
		IsTemplate:    repo.IsTemplate,
		HasWiki:       repo.UnitEnabled(ctx, unit.TypeWiki),
		HasIssues:     repo.UnitEnabled(ctx, unit.TypeIssues),
		HasProjects:   repo.UnitEnabled(ctx, unit.TypeProjects),
		DefaultBranch: repo.DefaultBranch,
	}

	// Get branches (limit to recent 20)
	branchNames, _, err := gitRepo.GetBranchNames(0, 20)
	if err == nil {
		response.Branches = branchNames
	}

	// Get recent tags
	tagInfos, _, err := gitRepo.GetTagInfos(1, 10)
	if err == nil {
		tagNames := make([]string, 0, len(tagInfos))
		for _, t := range tagInfos {
			tagNames = append(tagNames, t.Name)
		}
		response.Tags = tagNames
	}

	// Get last commit
	ref := req.Ref
	if ref == "" {
		ref = repo.DefaultBranch
	}
	if commit, err := gitRepo.GetBranchCommit(ref); err == nil {
		response.LastCommit.SHA = commit.ID.String()
		response.LastCommit.Message = strings.Split(commit.CommitMessage, "\n")[0]
		response.LastCommit.Author = commit.Author.Name
		response.LastCommit.Timestamp = commit.Author.When.Format("2006-01-02T15:04:05Z07:00")
	}

	// Analyze structure
	if commit, err := gitRepo.GetBranchCommit(ref); err == nil {
		if tree, err := commit.SubTree(""); err == nil {
			entries, _ := tree.ListEntries()

			for _, entry := range entries {
				name := entry.Name()
				if entry.IsDir() {
					response.Structure.TopLevelDirs = append(response.Structure.TopLevelDirs, name)
				} else {
					response.Structure.TopLevelFiles = append(response.Structure.TopLevelFiles, name)

					// Check for special files
					lowerName := strings.ToLower(name)
					if strings.HasPrefix(lowerName, "readme") {
						response.Structure.HasReadme = true
						response.Structure.ReadmePath = name
					}
					if strings.HasPrefix(lowerName, "license") || lowerName == "copying" {
						response.Structure.HasLicense = true
						response.Structure.LicensePath = name
					}
					if strings.HasPrefix(lowerName, "contributing") {
						response.Structure.HasContrib = true
						response.Structure.ContribPath = name
					}

					// Check for config files
					if isConfigFile(name) {
						response.Structure.ConfigFiles = append(response.Structure.ConfigFiles, name)
					}
				}
			}
		}

		// Count total files (approximate via tree walk, limit depth)
		response.Structure.FileCount = countFiles(commit, "", 0, 5)
	}

	// Get language stats from repo
	response.Structure.Languages = make(map[string]int64)
	if langs, err := repo_model.GetLanguageStats(ctx, repo); err == nil {
		for _, lang := range langs {
			response.Structure.Languages[lang.Language] = int64(lang.Percentage)
		}
	}

	// AI hints
	response.AIHints = detectProjectType(response.Structure.TopLevelFiles, response.Structure.ConfigFiles, response.Language)

	ctx.JSON(http.StatusOK, response)
}

// AINavigationRequest represents a request to understand repo navigation
type AINavigationRequest struct {
	Owner string `json:"owner" binding:"Required"`
	Repo  string `json:"repo" binding:"Required"`
	Ref   string `json:"ref"`
	Query string `json:"query"` // What the AI is looking for
}

// AINavigationResponse provides navigation hints for AI
type AINavigationResponse struct {
	// Directory tree (limited depth)
	Tree []TreeNode `json:"tree"`

	// Important paths
	ImportantPaths struct {
		Entrypoints []PathInfo `json:"entrypoints"`
		Config      []PathInfo `json:"config"`
		Tests       []PathInfo `json:"tests"`
		Docs        []PathInfo `json:"docs"`
	} `json:"important_paths"`

	// File type summary
	FileTypes map[string]int `json:"file_types"` // extension -> count
}

// TreeNode represents a node in the directory tree
type TreeNode struct {
	Path     string     `json:"path"`
	Name     string     `json:"name"`
	Type     string     `json:"type"` // "file" or "dir"
	Size     int64      `json:"size,omitempty"`
	Children []TreeNode `json:"children,omitempty"`
}

// PathInfo provides information about an important path
type PathInfo struct {
	Path        string `json:"path"`
	Description string `json:"description"`
	Priority    int    `json:"priority"` // 1-10, higher is more important
}

// GetAINavigation returns navigation hints for AI tools
func GetAINavigation(ctx *context.APIContext) {
	var req AINavigationRequest
	if err := json.NewDecoder(ctx.Req.Body).Decode(&req); err != nil {
		ctx.APIErrorWithCode(apierrors.ValidationFailed, map[string]any{
			"error": err.Error(),
		})
		return
	}

	// Get repository
	repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, req.Owner, req.Repo)
	if err != nil {
		if repo_model.IsErrRepoNotExist(err) {
			ctx.APIErrorWithCode(apierrors.RepoNotFound)
			return
		}
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}

	// Check access
	if repo.IsPrivate && !ctx.IsSigned {
		ctx.APIErrorWithCode(apierrors.PermAccessDenied)
		return
	}

	// Open git repo
	gitRepo, err := gitrepo.OpenRepository(ctx, repo)
	if err != nil {
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}
	defer gitRepo.Close()

	ref := req.Ref
	if ref == "" {
		ref = repo.DefaultBranch
	}

	response := AINavigationResponse{
		FileTypes: make(map[string]int),
	}

	commit, err := gitRepo.GetBranchCommit(ref)
	if err != nil {
		ctx.APIErrorWithCode(apierrors.RefNotFound)
		return
	}

	// Build tree (max depth 3)
	response.Tree = buildTree(commit, "", 0, 3)

	// Collect file types and identify important paths
	collectFileInfo(commit, "", &response)

	ctx.JSON(http.StatusOK, response)
}

// AIIssueContextRequest represents a request for issue context
type AIIssueContextRequest struct {
	Owner       string `json:"owner" binding:"Required"`
	Repo        string `json:"repo" binding:"Required"`
	IssueNumber int64  `json:"issue_number" binding:"Required"`
}

// AIIssueContextResponse provides rich context about an issue
type AIIssueContextResponse struct {
	// Issue details
	Number    int64    `json:"number"`
	Title     string   `json:"title"`
	Body      string   `json:"body"`
	State     string   `json:"state"`
	Labels    []string `json:"labels"`
	Author    string   `json:"author"`
	Assignees []string `json:"assignees"`
	CreatedAt string   `json:"created_at"`
	UpdatedAt string   `json:"updated_at"`

	// Comments
	Comments []struct {
		Author    string `json:"author"`
		Body      string `json:"body"`
		CreatedAt string `json:"created_at"`
	} `json:"comments"`

	// Related info
	RelatedIssues []struct {
		Number int64  `json:"number"`
		Title  string `json:"title"`
		State  string `json:"state"`
	} `json:"related_issues,omitempty"`

	// Code references (files mentioned in issue/comments)
	CodeReferences []string `json:"code_references,omitempty"`

	// AI hints
	AIHints struct {
		Category    string   `json:"category"` // "bug", "feature", "question", etc.
		Complexity  string   `json:"complexity"` // "simple", "moderate", "complex"
		SuggestedFiles []string `json:"suggested_files,omitempty"`
	} `json:"ai_hints"`
}

// GetAIIssueContext returns rich context about an issue for AI tools
func GetAIIssueContext(ctx *context.APIContext) {
	var req AIIssueContextRequest
	if err := json.NewDecoder(ctx.Req.Body).Decode(&req); err != nil {
		ctx.APIErrorWithCode(apierrors.ValidationFailed, map[string]any{
			"error": err.Error(),
		})
		return
	}

	// Get repository
	repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, req.Owner, req.Repo)
	if err != nil {
		if repo_model.IsErrRepoNotExist(err) {
			ctx.APIErrorWithCode(apierrors.RepoNotFound)
			return
		}
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}

	// Check access
	if repo.IsPrivate && !ctx.IsSigned {
		ctx.APIErrorWithCode(apierrors.PermAccessDenied)
		return
	}

	// Get issue
	issue, err := issues_model.GetIssueByIndex(ctx, repo.ID, req.IssueNumber)
	if err != nil {
		if issues_model.IsErrIssueNotExist(err) {
			ctx.APIErrorWithCode(apierrors.IssueNotFound, map[string]any{
				"issue_number": req.IssueNumber,
			})
			return
		}
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}

	// Load related data
	if err := issue.LoadPoster(ctx); err != nil {
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}
	if err := issue.LoadLabels(ctx); err != nil {
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}
	if err := issue.LoadAssignees(ctx); err != nil {
		ctx.APIErrorWithCode(apierrors.InternalError)
		return
	}

	response := AIIssueContextResponse{
		Number:    issue.Index,
		Title:     issue.Title,
		Body:      issue.Content,
		State:     map[bool]string{true: "closed", false: "open"}[issue.IsClosed],
		Author:    issue.Poster.Name,
		CreatedAt: issue.CreatedUnix.AsTime().Format("2006-01-02T15:04:05Z07:00"),
		UpdatedAt: issue.UpdatedUnix.AsTime().Format("2006-01-02T15:04:05Z07:00"),
	}

	// Labels
	for _, label := range issue.Labels {
		response.Labels = append(response.Labels, label.Name)
	}

	// Assignees
	for _, assignee := range issue.Assignees {
		response.Assignees = append(response.Assignees, assignee.Name)
	}

	// Get comments
	comments, err := issues_model.FindComments(ctx, &issues_model.FindCommentsOptions{
		IssueID: issue.ID,
		Type:    issues_model.CommentTypeComment,
	})
	if err == nil {
		for _, comment := range comments {
			if err := comment.LoadPoster(ctx); err != nil {
				continue
			}
			response.Comments = append(response.Comments, struct {
				Author    string `json:"author"`
				Body      string `json:"body"`
				CreatedAt string `json:"created_at"`
			}{
				Author:    comment.Poster.Name,
				Body:      comment.Content,
				CreatedAt: comment.CreatedUnix.AsTime().Format("2006-01-02T15:04:05Z07:00"),
			})
		}
	}

	// Extract code references from issue body and comments
	codeRefs := extractCodeReferences(issue.Content)
	for _, comment := range response.Comments {
		codeRefs = append(codeRefs, extractCodeReferences(comment.Body)...)
	}
	response.CodeReferences = uniqueStrings(codeRefs)

	// AI hints based on labels and content
	response.AIHints.Category = categorizeIssue(issue.Labels, issue.Title, issue.Content)
	response.AIHints.Complexity = estimateComplexity(issue.Content, len(response.Comments))

	ctx.JSON(http.StatusOK, response)
}

// Helper functions

func isConfigFile(name string) bool {
	configFiles := []string{
		"package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml",
		"go.mod", "go.sum",
		"Cargo.toml", "Cargo.lock",
		"requirements.txt", "setup.py", "pyproject.toml", "Pipfile",
		"Gemfile", "Gemfile.lock",
		"composer.json", "composer.lock",
		"pom.xml", "build.gradle", "build.gradle.kts",
		"CMakeLists.txt", "Makefile", "makefile",
		"Dockerfile", "docker-compose.yml", "docker-compose.yaml",
		".gitignore", ".gitattributes",
		"tsconfig.json", "jsconfig.json",
		".eslintrc", ".eslintrc.js", ".eslintrc.json",
		".prettierrc", ".prettierrc.js", ".prettierrc.json",
		"webpack.config.js", "vite.config.js", "rollup.config.js",
		".env.example", ".env.sample",
	}
	for _, cf := range configFiles {
		if name == cf {
			return true
		}
	}
	return false
}

func countFiles(commit *git.Commit, dir string, depth, maxDepth int) int {
	if depth > maxDepth {
		return 0
	}
	tree, err := commit.SubTree(dir)
	if err != nil {
		return 0
	}
	entries, _ := tree.ListEntries()
	count := 0
	for _, entry := range entries {
		if entry.IsDir() {
			subPath := entry.Name()
			if dir != "" {
				subPath = dir + "/" + entry.Name()
			}
			count += countFiles(commit, subPath, depth+1, maxDepth)
		} else {
			count++
		}
	}
	return count
}

func detectProjectType(files, configFiles []string, primaryLang string) struct {
	SuggestedEntryPoints []string `json:"suggested_entry_points"`
	ProjectType          string   `json:"project_type"`
	BuildSystem          string   `json:"build_system,omitempty"`
	TestFramework        string   `json:"test_framework,omitempty"`
} {
	hints := struct {
		SuggestedEntryPoints []string `json:"suggested_entry_points"`
		ProjectType          string   `json:"project_type"`
		BuildSystem          string   `json:"build_system,omitempty"`
		TestFramework        string   `json:"test_framework,omitempty"`
	}{
		ProjectType: "unknown",
	}

	for _, cf := range configFiles {
		switch cf {
		case "package.json":
			hints.BuildSystem = "npm/yarn"
			hints.SuggestedEntryPoints = append(hints.SuggestedEntryPoints, "package.json", "src/index.js", "src/index.ts")
		case "go.mod":
			hints.BuildSystem = "go modules"
			hints.SuggestedEntryPoints = append(hints.SuggestedEntryPoints, "go.mod", "main.go", "cmd/")
		case "Cargo.toml":
			hints.BuildSystem = "cargo"
			hints.SuggestedEntryPoints = append(hints.SuggestedEntryPoints, "Cargo.toml", "src/main.rs", "src/lib.rs")
		case "requirements.txt", "pyproject.toml":
			hints.BuildSystem = "pip/poetry"
			hints.SuggestedEntryPoints = append(hints.SuggestedEntryPoints, "setup.py", "main.py", "app.py")
		case "Makefile", "makefile":
			hints.BuildSystem = "make"
		}
	}

	// Detect project type
	for _, f := range files {
		if f == "main.go" || f == "main.rs" || f == "main.py" {
			hints.ProjectType = "application"
			break
		}
	}

	if hints.ProjectType == "unknown" {
		for _, cf := range configFiles {
			if cf == "setup.py" || cf == "Cargo.toml" {
				hints.ProjectType = "library"
				break
			}
		}
	}

	return hints
}

func buildTree(commit *git.Commit, dir string, depth, maxDepth int) []TreeNode {
	if depth >= maxDepth {
		return nil
	}

	tree, err := commit.SubTree(dir)
	if err != nil {
		return nil
	}

	entries, _ := tree.ListEntries()
	nodes := make([]TreeNode, 0, len(entries))

	for _, entry := range entries {
		node := TreeNode{
			Name: entry.Name(),
			Path: path.Join(dir, entry.Name()),
		}

		if entry.IsDir() {
			node.Type = "dir"
			node.Children = buildTree(commit, node.Path, depth+1, maxDepth)
		} else {
			node.Type = "file"
			node.Size = entry.Size()
		}

		nodes = append(nodes, node)
	}

	// Sort: directories first, then alphabetically
	sort.Slice(nodes, func(i, j int) bool {
		if nodes[i].Type != nodes[j].Type {
			return nodes[i].Type == "dir"
		}
		return nodes[i].Name < nodes[j].Name
	})

	return nodes
}

func collectFileInfo(commit *git.Commit, dir string, response *AINavigationResponse) {
	tree, err := commit.SubTree(dir)
	if err != nil {
		return
	}

	entries, _ := tree.ListEntries()
	for _, entry := range entries {
		fullPath := path.Join(dir, entry.Name())

		if entry.IsDir() {
			// Check for important directories
			name := strings.ToLower(entry.Name())
			if name == "test" || name == "tests" || name == "__tests__" || name == "spec" {
				response.ImportantPaths.Tests = append(response.ImportantPaths.Tests, PathInfo{
					Path:        fullPath,
					Description: "Test directory",
					Priority:    7,
				})
			}
			if name == "docs" || name == "documentation" {
				response.ImportantPaths.Docs = append(response.ImportantPaths.Docs, PathInfo{
					Path:        fullPath,
					Description: "Documentation directory",
					Priority:    6,
				})
			}
			if name == "src" || name == "lib" || name == "pkg" {
				response.ImportantPaths.Entrypoints = append(response.ImportantPaths.Entrypoints, PathInfo{
					Path:        fullPath,
					Description: "Source directory",
					Priority:    8,
				})
			}
		} else {
			// Count file extensions
			ext := strings.ToLower(path.Ext(entry.Name()))
			if ext != "" {
				response.FileTypes[ext]++
			}

			// Check for config files
			if isConfigFile(entry.Name()) {
				response.ImportantPaths.Config = append(response.ImportantPaths.Config, PathInfo{
					Path:        fullPath,
					Description: "Configuration file",
					Priority:    5,
				})
			}

			// Check for entry points
			name := strings.ToLower(entry.Name())
			if name == "main.go" || name == "main.rs" || name == "main.py" || name == "index.js" || name == "index.ts" {
				response.ImportantPaths.Entrypoints = append(response.ImportantPaths.Entrypoints, PathInfo{
					Path:        fullPath,
					Description: "Application entry point",
					Priority:    10,
				})
			}
		}
	}
}

func extractCodeReferences(text string) []string {
	// Simple extraction of file paths mentioned in text
	// Look for patterns like `path/to/file.ext` or file.ext
	refs := []string{}
	words := strings.Fields(text)
	for _, word := range words {
		// Clean up markdown code blocks
		word = strings.Trim(word, "`*_[]()\"'")
		if strings.Contains(word, ".") && (strings.Contains(word, "/") || strings.Contains(word, "\\")) {
			// Looks like a file path
			if len(word) > 3 && len(word) < 200 {
				refs = append(refs, word)
			}
		}
	}
	return refs
}

func uniqueStrings(input []string) []string {
	seen := make(map[string]bool)
	result := []string{}
	for _, s := range input {
		if !seen[s] {
			seen[s] = true
			result = append(result, s)
		}
	}
	return result
}

func categorizeIssue(labels []*issues_model.Label, title, body string) string {
	// Check labels first
	for _, label := range labels {
		name := strings.ToLower(label.Name)
		if strings.Contains(name, "bug") {
			return "bug"
		}
		if strings.Contains(name, "feature") || strings.Contains(name, "enhancement") {
			return "feature"
		}
		if strings.Contains(name, "question") || strings.Contains(name, "help") {
			return "question"
		}
		if strings.Contains(name, "documentation") || strings.Contains(name, "docs") {
			return "documentation"
		}
	}

	// Check title/body keywords
	combined := strings.ToLower(title + " " + body)
	if strings.Contains(combined, "error") || strings.Contains(combined, "crash") || strings.Contains(combined, "bug") {
		return "bug"
	}
	if strings.Contains(combined, "feature") || strings.Contains(combined, "add support") || strings.Contains(combined, "would be nice") {
		return "feature"
	}
	if strings.Contains(combined, "how to") || strings.Contains(combined, "how do i") || strings.Contains(combined, "?") {
		return "question"
	}

	return "general"
}

func estimateComplexity(body string, commentCount int) string {
	// Simple heuristics
	lines := len(strings.Split(body, "\n"))
	words := len(strings.Fields(body))

	if lines < 10 && words < 100 && commentCount < 3 {
		return "simple"
	}
	if lines > 50 || words > 500 || commentCount > 10 {
		return "complex"
	}
	return "moderate"
}
