package main

import (
	"fmt"
	"strings"
	"time"
)

func evaluateFile(path string, results []CommandResult, fileError string) TestResult {
	if fileError != "" {
		return TestResult{
			Path:         path,
			Passed:       false,
			CommandCount: len(results),
			Error:        fileError,
		}
	}

	var failures []FailedCommand

	for _, r := range results {
		cmd := r.Command
		outputMatches := matchOutput(cmd.Expected, r.Actual)
		exitCodeMismatch := !exitCodeOK(cmd.ExitCodeType, cmd.ExitCodeValue, r.ExitCode)

		if !outputMatches || exitCodeMismatch {
			var diffLines []DiffLine
			if !outputMatches {
				diffLines = diff(cmd.Expected, r.Actual)
			}
			failures = append(failures, FailedCommand{
				Result:           r,
				DiffLines:        diffLines,
				ExitCodeMismatch: exitCodeMismatch,
			})
		}
	}

	return TestResult{
		Path:         path,
		Passed:       len(failures) == 0,
		CommandCount: len(results),
		Failures:     failures,
	}
}

func formatFailure(t TestResult) string {
	var lines []string

	lines = append(lines, red("FAIL "+t.Path))

	if t.Error != "" {
		lines = append(lines, "  "+red(t.Error))
		return strings.Join(lines, "\n")
	}

	for _, f := range t.Failures {
		lines = append(lines, "")
		lines = append(lines, "  "+dim("$")+" "+f.Result.Command.Cmd)

		if len(f.DiffLines) > 0 {
			lines = append(lines, red("  expected:"))
			for _, dl := range f.DiffLines {
				switch dl.Kind {
				case "expected":
					lines = append(lines, red("  > ")+dl.Text)
				case "equal":
					lines = append(lines, "    "+dl.Text)
				case "context":
					lines = append(lines, "    "+dim(dl.Text))
				}
			}
			lines = append(lines, green("  actual:"))
			for _, dl := range f.DiffLines {
				switch dl.Kind {
				case "actual":
					lines = append(lines, green("  > ")+dl.Text)
				case "equal":
					lines = append(lines, "    "+dl.Text)
				case "context":
					lines = append(lines, "    "+dim(dl.Text))
				}
			}
		}

		if f.ExitCodeMismatch {
			cmd := f.Result.Command
			var expectedStr string
			switch cmd.ExitCodeType {
			case ExitCodeNone:
				expectedStr = "0"
			case ExitCodeWildcard:
				expectedStr = "non-zero"
			case ExitCodeExact:
				expectedStr = fmt.Sprintf("%d", cmd.ExitCodeValue)
			}
			lines = append(lines, red(fmt.Sprintf("  expected exit code: %s", expectedStr)))
			lines = append(lines, green(fmt.Sprintf("  actual exit code: %d", f.Result.ExitCode)))
		}
	}

	return strings.Join(lines, "\n")
}

func formatSummary(results []TestResult, elapsed time.Duration) string {
	totalCommands := 0
	failedCommands := 0
	errorCount := 0
	for _, r := range results {
		if r.Error != "" {
			errorCount++
			continue
		}
		totalCommands += r.CommandCount
		failedCommands += len(r.Failures)
	}
	passedCommands := totalCommands - failedCommands

	var parts []string
	if passedCommands > 0 {
		parts = append(parts, green(fmt.Sprintf("%d passed", passedCommands)))
	}
	if failedCommands > 0 {
		parts = append(parts, red(fmt.Sprintf("%d failed", failedCommands)))
	}
	if errorCount > 0 {
		parts = append(parts, red(fmt.Sprintf("%d errored", errorCount)))
	}

	var timeStr string
	if elapsed < time.Second {
		timeStr = fmt.Sprintf("%dms", elapsed.Milliseconds())
	} else {
		timeStr = fmt.Sprintf("%.1fs", elapsed.Seconds())
	}

	return fmt.Sprintf("%s in %s", strings.Join(parts, ", "), timeStr)
}
