package infra

import (
	"fmt"
	"math"
	"os"
	"path/filepath"
	"strings"
)

// AuditE2ECoverage walks each directory in roots looking for app.md files and
// reports which ones declare e2e_checks in their YAML frontmatter.
//
// For each app.md found, it reads the file and searches for the substring
// "e2e_checks:" inside the first frontmatter block (between the opening and
// closing "---" delimiters). Files that contain the key count as covered.
//
// Parameters:
//   - roots: slice of directory paths to scan (e.g. ["apps", "cpp/apps", "projects"])
//
// Returns an E2ECoverageReport with total, with_checks, missing paths and
// coverage_pct rounded to 2 decimal places. Returns a non-nil error only when
// filesystem I/O fails for a reason other than a missing/empty root.
func AuditE2ECoverage(roots []string) (E2ECoverageReport, error) {
	var report E2ECoverageReport

	for _, root := range roots {
		info, err := os.Stat(root)
		if err != nil {
			if os.IsNotExist(err) {
				continue
			}
			return report, fmt.Errorf("audit_e2e_coverage: stat %q: %w", root, err)
		}
		if !info.IsDir() {
			continue
		}

		walkErr := filepath.WalkDir(root, func(p string, d os.DirEntry, we error) error {
			if we != nil {
				// Skip unreadable entries without aborting the whole walk.
				return nil
			}
			if d.IsDir() {
				return nil
			}
			if filepath.Base(p) != "app.md" {
				return nil
			}

			report.Total++

			data, err := os.ReadFile(p)
			if err != nil {
				// Count but mark as missing — we cannot determine coverage.
				report.Missing = append(report.Missing, p)
				return nil
			}

			if hasE2EChecks(string(data)) {
				report.WithChecks++
			} else {
				report.Missing = append(report.Missing, p)
			}
			return nil
		})
		if walkErr != nil {
			return report, fmt.Errorf("audit_e2e_coverage: walk %q: %w", root, walkErr)
		}
	}

	report.CoveragePct = computeCoveragePct(report.WithChecks, report.Total)
	return report, nil
}

// hasE2EChecks returns true when the app.md content contains "e2e_checks:"
// inside the YAML frontmatter block (between the first pair of "---" lines).
// If the file has no frontmatter, it falls back to a whole-file scan so that
// non-standard formats are also handled gracefully.
func hasE2EChecks(content string) bool {
	const key = "e2e_checks:"

	if !strings.HasPrefix(content, "---") {
		// No frontmatter delimiter — scan full content.
		return strings.Contains(content, key)
	}

	// Skip the opening "---".
	rest := content[3:]
	if strings.HasPrefix(rest, "\r\n") {
		rest = rest[2:]
	} else if strings.HasPrefix(rest, "\n") {
		rest = rest[1:]
	}

	// Find the closing "---".
	end := strings.Index(rest, "\n---")
	if end < 0 {
		// Malformed frontmatter — scan full content.
		return strings.Contains(content, key)
	}

	frontmatter := rest[:end]
	return strings.Contains(frontmatter, key)
}

// computeCoveragePct returns (withChecks/total)*100 rounded to 2 decimal
// places. Returns 0.0 when total is zero to avoid division by zero.
func computeCoveragePct(withChecks, total int) float64 {
	if total == 0 {
		return 0.0
	}
	raw := float64(withChecks) / float64(total) * 100.0
	return math.Round(raw*100) / 100
}
