package connector_inbound_routing

import (
	"context"
	"errors"
	"fmt"
	"regexp"
	"testing"

	sqlmock "github.com/DATA-DOG/go-sqlmock"
)

// Compile-time interface check.
var _ RouteResolver = (*DBRouteResolver)(nil)

func TestDBRouteResolver_ResolveFlow_HappyPath(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery(regexp.QuoteMeta(loadRouteSQL)).
		WithArgs("install-abc", "contact.created").
		WillReturnRows(sqlmock.NewRows([]string{"FlowDeveloperName", "Version"}).AddRow("ContactCreatedHandler", int64(1)))

	r := NewDBRouteResolver(db)
	version, flow, err := r.ResolveFlow(context.Background(), "acme-connector", "install-abc", "contact.created")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if flow != "ContactCreatedHandler" {
		t.Errorf("expected flow=%q, got %q", "ContactCreatedHandler", flow)
	}
	if version != 1 {
		t.Errorf("expected version=1, got %d", version)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("unmet expectations: %v", err)
	}
}

func TestDBRouteResolver_ResolveFlow_NotFound_ReturnsErrRouteNotDeclared(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery(regexp.QuoteMeta(loadRouteSQL)).
		WithArgs("install-unknown", "contact.created").
		WillReturnRows(sqlmock.NewRows([]string{"FlowDeveloperName", "Version"}))

	r := NewDBRouteResolver(db)
	_, _, err = r.ResolveFlow(context.Background(), "unknown-connector", "install-unknown", "contact.created")
	if !errors.Is(err, ErrRouteNotDeclared) {
		t.Errorf("expected ErrRouteNotDeclared, got %v", err)
	}
}

func TestDBRouteResolver_ResolveFlow_DBError_Propagates(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	dbErr := fmt.Errorf("connection refused")
	mock.ExpectQuery(regexp.QuoteMeta(loadRouteSQL)).
		WithArgs("install-abc", "contact.created").
		WillReturnError(dbErr)

	r := NewDBRouteResolver(db)
	_, _, err = r.ResolveFlow(context.Background(), "acme-connector", "install-abc", "contact.created")
	if err == nil {
		t.Fatal("expected DB error, got nil")
	}
	if errors.Is(err, ErrRouteNotDeclared) {
		t.Error("DB error should not be wrapped as ErrRouteNotDeclared")
	}
}

func TestDBRouteResolver_ResolveFlowByVersion_HappyPath(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery(regexp.QuoteMeta(loadRouteByVersionSQL)).
		WithArgs("install-abc", "contact.created", int64(2)).
		WillReturnRows(routeVersionRows().AddRow("ContactCreatedV2", "", "", "", 0, "", "", int64(0)))

	r := NewDBRouteResolver(db)
	flow, err := r.ResolveFlowByVersion(context.Background(), "install-abc", "contact.created", 2)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if flow != "ContactCreatedV2" {
		t.Errorf("expected flow=%q, got %q", "ContactCreatedV2", flow)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("unmet expectations: %v", err)
	}
}

func TestDBRouteResolver_ResolveFlowByVersion_SweptRow_ReturnsErrRouteVersionSwept(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	// Row exists but IsDeleted=1 — version was soft-deleted by the pack sweeper.
	// DBRouteResolver must return ErrRouteVersionSwept (not ErrRouteNotDeclared,
	// not a silent flow return).
	mock.ExpectQuery(regexp.QuoteMeta(loadRouteByVersionSQL)).
		WithArgs("install-abc", "contact.created", int64(1)).
		WillReturnRows(routeVersionRows().AddRow("ContactCreatedV1", "", "", "", 0, "", "", int64(1)))

	r := NewDBRouteResolver(db)
	flow, err := r.ResolveFlowByVersion(context.Background(), "install-abc", "contact.created", 1)
	if !errors.Is(err, ErrRouteVersionSwept) {
		t.Errorf("expected ErrRouteVersionSwept for swept row, got err=%v flow=%q", err, flow)
	}
	if flow != "" {
		t.Errorf("expected empty flow on ErrRouteVersionSwept, got %q", flow)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("unmet expectations: %v", err)
	}
}

func TestDBRouteResolver_ResolveFlowByVersion_NotFound_ReturnsErrRouteNotDeclared(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery(regexp.QuoteMeta(loadRouteByVersionSQL)).
		WithArgs("install-abc", "contact.created", int64(99)).
		WillReturnRows(routeVersionRows())

	r := NewDBRouteResolver(db)
	_, err = r.ResolveFlowByVersion(context.Background(), "install-abc", "contact.created", 99)
	if !errors.Is(err, ErrRouteNotDeclared) {
		t.Errorf("expected ErrRouteNotDeclared, got %v", err)
	}
}
