package services_test

import (
	"context"
	"encoding/json"
	"regexp"
	"testing"
	"time"

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

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

	svc := services.NewAdminService(db)
	ctx := context.Background()

	// 1. UpdateTag
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, name, category, active, created_at, updated_at FROM job_tags`)).
		WithArgs(10).
		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "category", "active", "created_at", "updated_at"}).
			AddRow(10, "Old Tag", "skill", true, time.Now(), time.Now()))

	mock.ExpectExec(regexp.QuoteMeta(`UPDATE job_tags SET`)).
		WithArgs("New Tag", false, sqlmock.AnyArg(), 10).
		WillReturnResult(sqlmock.NewResult(1, 1))

	name := "New Tag"
	active := false
	tag, err := svc.UpdateTag(ctx, 10, &name, &active)
	assert.NoError(t, err)
	assert.Equal(t, "New Tag", tag.Name)
	assert.Equal(t, false, tag.Active)

	// 2. GetEmailTemplate
	vars := []string{"name"}
	varsJSON, _ := json.Marshal(vars)
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, slug, subject, body_html, variables`)).
		WithArgs("welcome").
		WillReturnRows(sqlmock.NewRows([]string{"id", "slug", "subject", "body_html", "variables", "created_at", "updated_at"}).
			AddRow("tpl-1", "welcome", "Welcome", "<h1>Hi</h1>", varsJSON, time.Now(), time.Now()))

	tpl, err := svc.GetEmailTemplate(ctx, "welcome")
	assert.NoError(t, err)
	assert.Equal(t, "Welcome", tpl.Subject)

	// 3. UpdateEmailSettings
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, provider, smtp_host`)).
		WillReturnRows(sqlmock.NewRows([]string{"id", "provider", "smtp_host", "smtp_port", "smtp_user", "smtp_pass", "smtp_secure", "sender_name", "sender_email", "amqp_url", "is_active", "updated_at"}).
			AddRow("set-1", "smtp", "old.host", 587, "user", "pass", true, "Sender", "email", "amqp://", true, time.Now()))

	mock.ExpectExec(regexp.QuoteMeta(`UPDATE email_settings SET`)).
		WithArgs("smtp", "new.host", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), "set-1").
		WillReturnResult(sqlmock.NewResult(1, 1))

	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, provider, smtp_host`)).
		WillReturnRows(sqlmock.NewRows([]string{"id", "provider", "smtp_host", "smtp_port", "smtp_user", "smtp_pass", "smtp_secure", "sender_name", "sender_email", "amqp_url", "is_active", "updated_at"}).
			AddRow("set-1", "smtp", "new.host", 587, "user", "pass", true, "Sender", "email", "amqp://", true, time.Now()))

	host := "new.host"
	settingsReq := dto.UpdateEmailSettingsRequest{SMTPHost: &host}
	settings, err := svc.UpdateEmailSettings(ctx, settingsReq)
	assert.NoError(t, err)
	if assert.NotNil(t, settings.SMTPHost) {
		assert.Equal(t, "new.host", *settings.SMTPHost)
	}
}

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

	mock.ExpectQuery(regexp.QuoteMeta(`SELECT company_id, created_by, title`)).
		WithArgs("job-1").
		WillReturnRows(sqlmock.NewRows([]string{"company_id", "created_by", "title", "description", "salary_min", "salary_max", "salary_type", "employment_type", "work_mode", "working_hours", "location", "region_id", "city_id", "requirements", "benefits", "visa_support", "language_level"}).
			AddRow("cmp-1", "user-1", "Job 1", "Desc", 100.0, 200.0, "USD", "full-time", "remote", "40", "Loc", 1, 1, []byte("{}"), []byte("{}"), true, "en"))

	mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO jobs`)).
		WithArgs("cmp-1", "user-1", "Job 1", "Desc", 100.0, 200.0, "USD", "full-time", "remote", "40", "Loc", 1, 1, []byte("{}"), []byte("{}"), true, "en", "draft", false, sqlmock.AnyArg(), sqlmock.AnyArg()).
		WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("job-new"))

	job, err := svc.DuplicateJob(context.Background(), "job-1")
	assert.NoError(t, err)
	assert.Equal(t, "job-new", job.ID)
	assert.Equal(t, "draft", job.Status)
}

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

	// ListCompanies
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM companies`)).
		WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))

	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, name`)).
		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "slug", "type", "document", "address", "region_id", "city_id", "phone", "email", "website", "logo_url", "description", "active", "verified", "created_at", "updated_at"}).
			AddRow("cmp-1", "Company", "slug", "tech", "doc", "addr", 1, 1, "123", "email", "web", "logo", "desc", true, true, time.Now(), time.Now()))

	verified := true
	companies, total, err := svc.ListCompanies(context.Background(), &verified, 1, 10)
	assert.NoError(t, err)
	assert.Equal(t, 1, total)
	assert.Len(t, companies, 1)

	// ListUsers (with company filter)
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM users WHERE tenant_id = $1`)).
		WithArgs("cmp-1").
		WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))

	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, COALESCE(name, full_name, identifier, ''), email, role`)).
		WithArgs("cmp-1", 10, 0).
		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "email", "role", "status", "created_at"}).
			AddRow("u-1", "User", "email", "admin", "active", time.Now()))

	companyID := "cmp-1"
	users, total, err := svc.ListUsers(context.Background(), 1, 10, &companyID)
	assert.NoError(t, err)
	assert.Equal(t, 1, total)
	assert.Len(t, users, 1)

	// ListTags
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, name, category, active`)).
		WithArgs("skill").
		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "category", "active", "created_at", "updated_at"}).
			AddRow(1, "Go", "skill", true, time.Now(), time.Now()))

	cat := "skill"
	tags, err := svc.ListTags(context.Background(), &cat)
	assert.NoError(t, err)
	assert.Len(t, tags, 1)

	// CreateTag
	mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO job_tags`)).
		WithArgs("Java", "skill", true, sqlmock.AnyArg(), sqlmock.AnyArg()).
		WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(2))

	newTag, err := svc.CreateTag(context.Background(), "Java", "skill")
	assert.NoError(t, err)
	assert.Equal(t, "Java", newTag.Name)

	// UpdateCompany
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, name`)).
		WithArgs("cmp-1").
		WillReturnRows(sqlmock.NewRows([]string{"id", "name", "slug", "type", "document", "address", "region_id", "city_id", "phone", "email", "website", "logo_url", "description", "active", "verified", "created_at", "updated_at"}).
			AddRow("cmp-1", "Old Name", "slug", "tech", "doc", "addr", 1, 1, "123", "email", "web", "logo", "desc", true, true, time.Now(), time.Now()))

	mock.ExpectExec(regexp.QuoteMeta(`UPDATE companies`)).
		WithArgs("New Name", "slug", "tech", "doc", "addr", 1, 1, "123", "email", "web", "logo", "new desc", true, true, sqlmock.AnyArg(), "cmp-1").
		WillReturnResult(sqlmock.NewResult(1, 1))

	newName := "New Name"
	newDesc := "new desc"
	cmpReq := dto.UpdateCompanyRequest{Name: &newName, Description: &newDesc}
	updatedCmp, err := svc.UpdateCompany(context.Background(), "cmp-1", cmpReq)
	assert.NoError(t, err)
	assert.Equal(t, "New Name", updatedCmp.Name)
}
