package cmd

import (
	"fmt"
	"log"
	"time"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/go-git/go-git/v5/plumbing/storer"
	"github.com/spf13/cobra"
)

const churnBenchmarkContext = "High churn indicates instability and potential architectural issues (Microsoft Research)."

const (
	churnHealthyThreshold  = 5
	churnCautionThreshold  = 15
)

func processCommitDiffs(c *object.Commit, pathFilters []string) (int, int) {
	var additions, deletions int
	if c.NumParents() == 0 {
		return 0, 0
	}
	parents := c.Parents()
	defer parents.Close()
	parents.ForEach(func(parent *object.Commit) error {
		patch, err := parent.Patch(c)
		if err != nil {
			log.Printf("failed to generate patch between parent %s and commit %s: %v", parent.Hash.String(), c.Hash.String(), err)
			return nil
		}
		for _, stat := range patch.Stats() {
			if !matchesPathFilter(stat.Name, pathFilters) {
				continue
			}
			additions += stat.Addition
			deletions += stat.Deletion
		}
		return nil
	})
	return additions, deletions
}


// churnCmd represents the churn command
var churnCmd = &cobra.Command{
	Use:   "churn",
	Short: "Show additions vs deletions ratio in the repo",
	Long: `Analyze git history to show how much code was added vs deleted. 
This helps you understand whether your repo is growing sustainably 
or accumulating complexity.`,
	Run: func(cmd *cobra.Command, args []string) {
		// Parse flags
		lastArg, _ := cmd.Flags().GetString("last")
		pathFilters, source := getConfigPaths(cmd, "churn.paths")
		
		// Print configuration scope
		printCommandScope(cmd, "churn", lastArg, pathFilters, source)
		
		repo, err := git.PlainOpen(".")
		if err != nil {
			log.Fatalf("Could not open repository: %v", err)
		}

		ref, err := repo.Head()
		if err != nil {
			log.Fatalf("Could not get HEAD: %v", err)
		}

		cIter, err := repo.Log(&git.LogOptions{From: ref.Hash()})
		if err != nil {
			log.Fatalf("Could not get commits: %v", err)
		}

		since := time.Time{}
		if lastArg != "" {
			cutoff, err := parseDurationArg(lastArg)
			if err != nil {
				log.Fatalf("Could not parse --last argument: %v", err)
			}
			since = cutoff
		}

		var additions, deletions int
		err = cIter.ForEach(func(c *object.Commit) error {
			if !since.IsZero() && c.Committer.When.Before(since) {
				return storer.ErrStop
			}
			a, d := processCommitDiffs(c, pathFilters)
			additions += a
			deletions += d
			return nil
		})
		if err != nil {
			log.Fatalf("Error walking commits: %v", err)
		}

		// Calculate total LOC from latest commit's tree
		headCommit, err := repo.CommitObject(ref.Hash())
		if err != nil {
			log.Fatalf("Could not get HEAD commit: %v", err)
		}
		tree, err := headCommit.Tree()
		if err != nil {
			log.Fatalf("Could not get HEAD tree: %v", err)
		}
		var totalLOC int
		err = tree.Files().ForEach(func(f *object.File) error {
			// Skip binary files
			isBinary, err := f.IsBinary()
			if err != nil {
				return nil
			}
			if isBinary {
				return nil
			}
			// Optionally filter by path
			if !matchesPathFilter(f.Name, pathFilters) {
				return nil
			}
			content, err := f.Contents()
			if err != nil {
				return nil
			}
			totalLOC += countLines(content)
			return nil
		})
		if err != nil {
			log.Fatalf("Could not count LOC: %v", err)
		}

		churnPercent := 0.0
		if totalLOC > 0 {
			churnPercent = float64(additions+deletions) / float64(totalLOC) * 100
		}
		status := ""
		if churnPercent <= float64(churnHealthyThreshold) {
			status = fmt.Sprintf("Healthy (≤%d%%)", churnHealthyThreshold)
		} else if churnPercent <= float64(churnCautionThreshold) {
			status = fmt.Sprintf("Caution (%d–%d%%)", churnHealthyThreshold, churnCautionThreshold)
		} else {
			status = fmt.Sprintf("Warning (>%d%%)", churnCautionThreshold)
		}

		fmt.Printf("Additions vs Deletions:\n")
		fmt.Printf("- Additions: %d lines\n", additions)
		fmt.Printf("- Deletions: %d lines\n", deletions)
		fmt.Printf("- Total LOC: %d lines\n", totalLOC)
		fmt.Printf("Churn = (Additions + Deletions) / Total LOC\n")
		fmt.Printf("Churn: %.2f%% — %s\n", churnPercent, status)
		fmt.Println("Context:", churnBenchmarkContext)
	},
}

func init() {
	rootCmd.AddCommand(churnCmd)
	churnCmd.Flags().String("last", "", "Limit analysis to a timeframe (e.g. 7d, 2m, 1y)")
	churnCmd.Flags().StringSlice("path", []string{}, "Limit analysis to specific paths (can be specified multiple times)")
}