package checks

import (
	"context"

	"github.com/cloudflare/pint/internal/diags"
	"github.com/cloudflare/pint/internal/discovery"
	"github.com/cloudflare/pint/internal/parser"
	"github.com/cloudflare/pint/internal/parser/source"
)

const (
	ImpossibleCheckName = "promql/impossible"
)

func NewImpossibleCheck() ImpossibleCheck {
	return ImpossibleCheck{}
}

type ImpossibleCheck struct{}

func (c ImpossibleCheck) Meta() CheckMeta {
	return CheckMeta{
		States: []discovery.ChangeType{
			discovery.Noop,
			discovery.Added,
			discovery.Modified,
			discovery.Moved,
		},
		Online:        false,
		AlwaysEnabled: false,
	}
}

func (c ImpossibleCheck) String() string {
	return ImpossibleCheckName
}

func (c ImpossibleCheck) Reporter() string {
	return ImpossibleCheckName
}

func (c ImpossibleCheck) Check(_ context.Context, entry *discovery.Entry, _ []*discovery.Entry) (problems []Problem) {
	expr := entry.Rule.Expr()
	if expr.SyntaxError() != nil {
		return problems
	}

	for _, src := range expr.Source() {
		src.WalkSources(func(s source.Source, _ *source.Join, _ *source.Unless) {
			problems = append(problems, c.checkSource(expr, s)...)
		})
	}

	return problems
}

func (c ImpossibleCheck) checkSource(expr *parser.PromQLExpr, s source.Source) (problems []Problem) {
	if s.DeadInfo != nil {
		problems = append(problems, Problem{
			Anchor:   AnchorAfter,
			Lines:    expr.Value.Pos.Lines(),
			Reporter: c.Reporter(),
			Summary:  "dead code in query",
			Details:  "",
			Diagnostics: []diags.Diagnostic{
				{
					Pos:         expr.Value.Pos,
					FirstColumn: int(s.DeadInfo.Fragment.Start) + 1,
					LastColumn:  int(s.DeadInfo.Fragment.End),
					Message:     s.DeadInfo.Reason,
					Kind:        diags.Issue,
				},
			},
			Severity: Warning,
		})
	}
	for _, dl := range s.DeadLabels {
		problems = append(problems, Problem{
			Anchor:   AnchorAfter,
			Lines:    expr.Value.Pos.Lines(),
			Reporter: c.Reporter(),
			Summary:  dl.Kind.String(),
			Details:  "",
			Diagnostics: []diags.Diagnostic{
				{
					Pos:         expr.Value.Pos,
					FirstColumn: int(dl.UsageFragment.Start) + 1,
					LastColumn:  int(dl.UsageFragment.End),
					Message:     dl.Reason,
					Kind:        diags.Issue,
				},
				{
					Pos:         expr.Value.Pos,
					FirstColumn: int(dl.LabelFragment.Start) + 1,
					LastColumn:  int(dl.LabelFragment.End),
					Message:     dl.LabelReason,
					Kind:        diags.Context,
				},
			},
			Severity: Warning,
		})
	}
	return problems
}
