package agent

import "strings"

// BuildFailureKind classifies a failed tool result by its error *content*,
// not just the tool name. This lets the agent loop inject recovery guidance
// targeted at the specific mistake instead of generic per-tool advice,
// reducing the number of wasted iterations spent re-diagnosing the error.
type BuildFailureKind int

const (
	// FailureUnknown means the error could not be classified — callers should
	// fall back to generic per-tool recovery guidance.
	FailureUnknown         BuildFailureKind = iota
	FailureMissingModule                    // import points at a module/path that does not exist
	FailureBadExport                        // importing a symbol the module does not export
	FailureSyntax                           // unbalanced brackets / unclosed tags / bad tokens
	FailureDuplicateExport                  // more than one default export in a file
	FailureTypeMismatch                     // a value assigned to an incompatible type
	FailureStaleEdit                        // editFile search string no longer matches the file
	FailureMissingRoute                     // a page exists but is not registered in the router
)

// String returns a stable snake_case label, useful for logs and metrics.
func (k BuildFailureKind) String() string {
	switch k {
	case FailureMissingModule:
		return "missing_module"
	case FailureBadExport:
		return "bad_export"
	case FailureSyntax:
		return "syntax_error"
	case FailureDuplicateExport:
		return "duplicate_export"
	case FailureTypeMismatch:
		return "type_mismatch"
	case FailureStaleEdit:
		return "stale_edit"
	case FailureMissingRoute:
		return "missing_route"
	default:
		return "unknown"
	}
}

// ClassifyFailure inspects a failed tool result and returns the most specific
// BuildFailureKind it can identify. It is a best-effort heuristic: when the
// error does not match a known pattern it returns FailureUnknown, and the
// caller should fall back to generic guidance. Matching is case-insensitive.
func ClassifyFailure(toolName, errorMsg string) BuildFailureKind {
	lower := strings.ToLower(errorMsg)

	switch {
	case strings.Contains(lower, "cannot find module"):
		return FailureMissingModule
	case strings.Contains(lower, "has no exported member"):
		return FailureBadExport
	case strings.Contains(lower, "more than one default export"),
		strings.Contains(lower, "multiple default export"):
		return FailureDuplicateExport
	case strings.Contains(lower, "is not assignable to"):
		return FailureTypeMismatch
	case strings.Contains(lower, "unexpected token"),
		strings.Contains(lower, "expression expected"),
		strings.Contains(lower, "unterminated"),
		strings.Contains(lower, "unclosed"):
		// Note: a bare "expected" is intentionally NOT matched here — TS uses
		// it for arity/type errors ("Expected 0 arguments...") too, which
		// would misclassify as a syntax error.
		return FailureSyntax
	case strings.Contains(lower, "search string not found"),
		strings.Contains(lower, "search text not found"),
		strings.Contains(lower, "could not find the text"),
		strings.Contains(lower, "no match for search"):
		return FailureStaleEdit
	}

	if toolName == "verifyIntegration" {
		return FailureMissingRoute
	}
	return FailureUnknown
}

// RecoveryHint returns precise, failure-specific recovery guidance for a kind.
// It returns an empty string for FailureUnknown so the caller knows to fall
// back to its generic per-tool guidance.
func RecoveryHint(kind BuildFailureKind) string {
	switch kind {
	case FailureMissingModule:
		return "SPECIFIC FIX (missing module): the import path does not resolve. " +
			"Use listFiles to confirm the file exists and check the exact path/case. " +
			"shadcn components import from @/components/ui/<name>; verify <name> is in " +
			"template.json's shadcn_components array before importing it."
	case FailureBadExport:
		return "SPECIFIC FIX (bad export): you are importing a name the module does not " +
			"export. readFile the module and import only a name it actually exports. " +
			"Default vs named exports matter — match the export style exactly."
	case FailureSyntax:
		return "SPECIFIC FIX (syntax error): go to the file:line:column in the error and " +
			"readFile it. Count brackets { } ( ) and JSX tags < > — every opener needs a " +
			"closer. Fix ONLY that one location, then re-verify."
	case FailureDuplicateExport:
		return "SPECIFIC FIX (duplicate default export): the file has more than one " +
			"`export default`. Keep exactly one; convert the others to named exports or " +
			"remove them."
	case FailureTypeMismatch:
		return "SPECIFIC FIX (type mismatch): a value does not match the expected type. " +
			"readFile the component's prop interface and pass the correct shape — do not " +
			"cast with `as any`."
	case FailureStaleEdit:
		return "SPECIFIC FIX (stale edit): the search string no longer matches the file " +
			"(it changed since you last read it). readFile the file NOW, copy the exact " +
			"current text — including whitespace — as the search string, or use createFile " +
			"to rewrite the file."
	case FailureMissingRoute:
		return "SPECIFIC FIX (missing route): a page file exists but is not registered. " +
			"Open src/routes.tsx (or template.json's routes_file), import the page, and add " +
			"a route entry with path, label, element, showInNav and layout."
	default:
		return ""
	}
}
