package services_test

import (
	"context"
	"database/sql"
	"regexp"
	"testing"
	"time"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/rede5/gohorsejobs/backend/internal/infrastructure/persistence/postgres"
	"github.com/rede5/gohorsejobs/backend/internal/services"
	"github.com/stretchr/testify/assert"
)

func TestAuxiliaryServices_WithMockDB(t *testing.T) {
	db, mock, err := sqlmock.New()
	assert.NoError(t, err)
	defer db.Close()

	creds := services.NewCredentialsService(db)

	// 1. CPanel
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT encrypted_payload FROM external_services_credentials`)).
		WillReturnError(sql.ErrNoRows)

	cp := services.NewCPanelService(creds)
	_, err = cp.GetConfig(context.Background())
	assert.Error(t, err)

	// 2. Cloudflare
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT encrypted_payload FROM external_services_credentials`)).
		WillReturnError(sql.ErrNoRows)

	cf := services.NewCloudflareService(creds)
	err = cf.PurgeCache(context.Background())
	assert.Error(t, err)

	// 3. Appwrite
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT encrypted_payload FROM external_services_credentials`)).
		WillReturnError(sql.ErrNoRows)

	aw := services.NewAppwriteService(creds)
	err = aw.PushMessage(context.Background(), "msg1", "conv1", "user1", "Hello")
	assert.Error(t, err)

	// 4. FCM
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT encrypted_payload FROM external_services_credentials`)).
		WillReturnError(sql.ErrNoRows)

	fcm := services.NewFCMService(creds)
	err = fcm.SendPush(context.Background(), "token", "title", "body", map[string]string{})
	assert.Error(t, err)

	// FCM Subscribe
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT encrypted_payload FROM external_services_credentials`)).
		WillReturnError(sql.ErrNoRows)
	err = fcm.SubscribeToTopic(context.Background(), []string{"token"}, "topic")
	assert.Error(t, err)
}

func TestLocationService_WithMockRepo(t *testing.T) {
	db, mock, err := sqlmock.New()
	assert.NoError(t, err)
	defer db.Close()

	repo := postgres.NewLocationRepository(db)
	svc := services.NewLocationService(repo)

	// ListCountries
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, name, iso2, iso3, phonecode, currency, emoji, emoji_u, created_at, updated_at FROM countries`)).
		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "iso2", "iso3", "phonecode", "currency", "emoji", "emoji_u", "created_at", "updated_at"}).
			AddRow(1, "Brazil", "BR", "BRA", "55", "BRL", "", "", time.Now(), time.Now()))

	countries, err := svc.ListCountries(context.Background())
	assert.NoError(t, err)
	assert.Len(t, countries, 1)

	// ListStates
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, name, country_id, country_code, iso2, type, latitude, longitude FROM states`)).
		WithArgs(int64(1)).
		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "country_id", "country_code", "iso2", "type", "latitude", "longitude"}).
			AddRow(1, "Sao Paulo", 1, "BR", "SP", "state", 0.0, 0.0))

	states, err := svc.ListStates(context.Background(), 1)
	assert.NoError(t, err)
	assert.Len(t, states, 1)

	// ListCities
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, name, state_id, country_id, latitude, longitude FROM cities`)).
		WithArgs(int64(1)).
		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "state_id", "country_id", "latitude", "longitude"}).
			AddRow(1, "Sorocaba", 1, 1, 0.0, 0.0))

	cities, err := svc.ListCities(context.Background(), 1)
	assert.NoError(t, err)
	assert.Len(t, cities, 1)

	// Search
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, name, 'state' as type, country_id, NULL as state_id, '' as region_name FROM states WHERE country_id = $1 AND name ILIKE $2 UNION ALL SELECT c.id, c.name, 'city' as type, c.country_id, c.state_id, s.name as region_name FROM cities c JOIN states s ON c.state_id = s.id WHERE c.country_id = $1 AND c.name ILIKE $2`)).
		WithArgs(int64(1), "%sorocaba%").
		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "type", "country_id", "state_id", "region_name"}).
			AddRow(1, "Sorocaba", "city", 1, 1, "SP"))

	results, err := svc.Search(context.Background(), "sorocaba", 1)
	assert.NoError(t, err)
	assert.Len(t, results, 1)
}

func TestNotificationService_SaveFCMToken(t *testing.T) {
	db, mock, err := sqlmock.New()
	assert.NoError(t, err)
	defer db.Close()

	svc := services.NewNotificationService(db, nil)

	mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO fcm_tokens`)).
		WithArgs("user-1", "token-123", "web").
		WillReturnResult(sqlmock.NewResult(1, 1))

	err = svc.SaveFCMToken(context.Background(), "user-1", "token-123", "web")
	assert.NoError(t, err)
}

func TestChatService_Constructors(t *testing.T) {
	db, _, _ := sqlmock.New()
	defer db.Close()

	chat := services.NewChatService(db, nil)
	assert.NotNil(t, chat)
}
