package logql

import (
	"fmt"

	"github.com/grafana/loki/pkg/logql/log"
	"github.com/prometheus/prometheus/pkg/labels"
)

// ParseError is what is returned when we failed to parse.
type ParseError struct {
	msg       string
	line, col int
}

func (p ParseError) Error() string {
	if p.col == 0 && p.line == 0 {
		return fmt.Sprintf("parse error : %s", p.msg)
	}
	return fmt.Sprintf("parse error at line %d, col %d: %s", p.line, p.col, p.msg)
}

func newParseError(msg string, line, col int) ParseError {
	return ParseError{
		msg:  msg,
		line: line,
		col:  col,
	}
}

func newStageError(expr Expr, err error) ParseError {
	return ParseError{
		msg:  fmt.Sprintf(`stage '%s' : %s`, expr, err),
		line: 0,
		col:  0,
	}
}

// IsParseError returns true if the err is a ast parsing error.
func IsParseError(err error) bool {
	_, ok := err.(ParseError)
	return ok
}

type pipelineError struct {
	metric    labels.Labels
	errorType string
}

func newPipelineErr(metric labels.Labels) *pipelineError {
	return &pipelineError{
		metric:    metric,
		errorType: metric.Get(log.ErrorLabel),
	}
}

func (e pipelineError) Error() string {
	return fmt.Sprintf(
		"pipeline error: '%s' for series: '%s'.\n"+
			"Use a label filter to intentionally skip this error. (e.g | __error__!=\"%s\").\n"+
			"To skip all potential errors you can match empty errors.(e.g __error__=\"\")\n"+
			"The label filter can also be specified after unwrap. (e.g | unwrap latency | __error__=\"\" )\n",
		e.errorType, e.metric, e.errorType)
}

// IsPipelineError tells if the error is generated by a Pipeline.
func IsPipelineError(err error) bool {
	_, ok := err.(*pipelineError)
	return ok
}
