package services

import (
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/treytartt/honeydue-api/internal/dto/requests"
	"github.com/treytartt/honeydue-api/internal/models"
	"github.com/treytartt/honeydue-api/internal/repositories"
	"github.com/treytartt/honeydue-api/internal/testutil"
)

func setupContractorService(t *testing.T) (*ContractorService, *repositories.ContractorRepository, *repositories.ResidenceRepository) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)
	return service, contractorRepo, residenceRepo
}

// === CreateContractor ===

func TestContractorService_CreateContractor(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")

	req := &requests.CreateContractorRequest{
		ResidenceID: &residence.ID,
		Name:        "Bob's Plumbing",
		Phone:       "555-1234",
		Email:       "bob@plumbing.com",
	}

	resp, err := service.CreateContractor(req, user.ID)
	require.NoError(t, err)
	assert.NotNil(t, resp)
	assert.Equal(t, "Bob's Plumbing", resp.Name)
	assert.Equal(t, "555-1234", resp.Phone)
	assert.Equal(t, "bob@plumbing.com", resp.Email)
}

func TestContractorService_CreateContractor_Personal(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")

	// No residence ID - personal contractor
	req := &requests.CreateContractorRequest{
		Name: "Personal Handyman",
	}

	resp, err := service.CreateContractor(req, user.ID)
	require.NoError(t, err)
	assert.Equal(t, "Personal Handyman", resp.Name)
}

func TestContractorService_CreateContractor_AccessDenied(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")

	req := &requests.CreateContractorRequest{
		ResidenceID: &residence.ID,
		Name:        "Unauthorized Contractor",
	}

	_, err := service.CreateContractor(req, other.ID)
	testutil.AssertAppError(t, err, http.StatusForbidden, "error.residence_access_denied")
}

func TestContractorService_CreateContractor_WithFavorite(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")

	isFav := true
	req := &requests.CreateContractorRequest{
		ResidenceID: &residence.ID,
		Name:        "Fav Plumber",
		IsFavorite:  &isFav,
	}

	resp, err := service.CreateContractor(req, user.ID)
	require.NoError(t, err)
	assert.True(t, resp.IsFavorite)
}

// === GetContractor ===

func TestContractorService_GetContractor(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Test Contractor")

	resp, err := service.GetContractor(contractor.ID, user.ID)
	require.NoError(t, err)
	assert.Equal(t, contractor.ID, resp.ID)
	assert.Equal(t, "Test Contractor", resp.Name)
}

func TestContractorService_GetContractor_NotFound(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")

	_, err := service.GetContractor(9999, user.ID)
	testutil.AssertAppError(t, err, http.StatusNotFound, "error.contractor_not_found")
}

func TestContractorService_GetContractor_AccessDenied(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, owner.ID, "Private Contractor")

	_, err := service.GetContractor(contractor.ID, other.ID)
	testutil.AssertAppError(t, err, http.StatusForbidden, "error.contractor_access_denied")
}

func TestContractorService_GetContractor_SharedUserHasAccess(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	shared := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
	residenceRepo.AddUser(residence.ID, shared.ID)
	contractor := testutil.CreateTestContractor(t, db, residence.ID, owner.ID, "Shared Contractor")

	resp, err := service.GetContractor(contractor.ID, shared.ID)
	require.NoError(t, err)
	assert.Equal(t, "Shared Contractor", resp.Name)
}

// === ListContractors ===

func TestContractorService_ListContractors(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
	testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Contractor 1")
	testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Contractor 2")

	resp, err := service.ListContractors(user.ID)
	require.NoError(t, err)
	assert.Len(t, resp, 2)
}

// === DeleteContractor ===

func TestContractorService_DeleteContractor(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "To Delete")

	err := service.DeleteContractor(contractor.ID, user.ID)
	require.NoError(t, err)

	// Should not be found after deletion
	_, err = service.GetContractor(contractor.ID, user.ID)
	assert.Error(t, err)
}

func TestContractorService_DeleteContractor_NotFound(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")

	err := service.DeleteContractor(9999, user.ID)
	testutil.AssertAppError(t, err, http.StatusNotFound, "error.contractor_not_found")
}

func TestContractorService_DeleteContractor_AccessDenied(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, owner.ID, "Private Contractor")

	err := service.DeleteContractor(contractor.ID, other.ID)
	testutil.AssertAppError(t, err, http.StatusForbidden, "error.contractor_access_denied")
}

// === ToggleFavorite ===

func TestContractorService_ToggleFavorite(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Test Contractor")

	// Initially not favorite
	resp, err := service.GetContractor(contractor.ID, user.ID)
	require.NoError(t, err)
	assert.False(t, resp.IsFavorite)

	// Toggle to favorite
	resp, err = service.ToggleFavorite(contractor.ID, user.ID)
	require.NoError(t, err)
	assert.True(t, resp.IsFavorite)

	// Toggle back
	resp, err = service.ToggleFavorite(contractor.ID, user.ID)
	require.NoError(t, err)
	assert.False(t, resp.IsFavorite)
}

func TestContractorService_ToggleFavorite_NotFound(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")

	_, err := service.ToggleFavorite(9999, user.ID)
	testutil.AssertAppError(t, err, http.StatusNotFound, "error.contractor_not_found")
}

func TestContractorService_ToggleFavorite_AccessDenied(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, owner.ID, "Private Contractor")

	_, err := service.ToggleFavorite(contractor.ID, other.ID)
	testutil.AssertAppError(t, err, http.StatusForbidden, "error.contractor_access_denied")
}

// === ListContractorsByResidence ===

func TestContractorService_ListContractorsByResidence(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
	testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Contractor A")
	testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Contractor B")

	resp, err := service.ListContractorsByResidence(residence.ID, user.ID)
	require.NoError(t, err)
	assert.Len(t, resp, 2)
}

func TestContractorService_ListContractorsByResidence_AccessDenied(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")

	_, err := service.ListContractorsByResidence(residence.ID, other.ID)
	testutil.AssertAppError(t, err, http.StatusForbidden, "error.residence_access_denied")
}

// === GetContractorTasks ===

func TestContractorService_GetContractorTasks_NotFound(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")

	_, err := service.GetContractorTasks(9999, user.ID)
	testutil.AssertAppError(t, err, http.StatusNotFound, "error.contractor_not_found")
}

func TestContractorService_GetContractorTasks_AccessDenied(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, owner.ID, "Private Contractor")

	_, err := service.GetContractorTasks(contractor.ID, other.ID)
	testutil.AssertAppError(t, err, http.StatusForbidden, "error.contractor_access_denied")
}

func TestContractorService_GetContractorTasks_Empty(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Test Contractor")

	resp, err := service.GetContractorTasks(contractor.ID, user.ID)
	require.NoError(t, err)
	assert.Empty(t, resp)
}

// === GetSpecialties ===

func TestContractorService_GetSpecialties(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	resp, err := service.GetSpecialties()
	require.NoError(t, err)
	// SeedLookupData creates 4 specialties
	assert.Len(t, resp, 4)
}

// === UpdateContractor ===

func TestContractorService_UpdateContractor_NotFound(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")

	newName := "Won't Work"
	req := &requests.UpdateContractorRequest{Name: &newName}

	_, err := service.UpdateContractor(9999, user.ID, req)
	testutil.AssertAppError(t, err, http.StatusNotFound, "error.contractor_not_found")
}

func TestContractorService_UpdateContractor_AccessDenied(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, owner.ID, "Private Contractor")

	newName := "Hacked"
	req := &requests.UpdateContractorRequest{Name: &newName}

	_, err := service.UpdateContractor(contractor.ID, other.ID, req)
	testutil.AssertAppError(t, err, http.StatusForbidden, "error.contractor_access_denied")
}

func TestUpdateContractor_CrossResidence_Returns403(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	// Create two users: owner and attacker
	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	attacker := testutil.CreateTestUser(t, db, "attacker", "attacker@test.com", "password")

	// Owner creates a residence
	ownerResidence := testutil.CreateTestResidence(t, db, owner.ID, "Owner House")

	// Attacker creates a residence and a contractor in their residence
	attackerResidence := testutil.CreateTestResidence(t, db, attacker.ID, "Attacker House")
	contractor := testutil.CreateTestContractor(t, db, attackerResidence.ID, attacker.ID, "My Contractor")

	// Attacker tries to reassign their contractor to the owner's residence
	// This should be denied because the attacker does not have access to the owner's residence
	newResidenceID := ownerResidence.ID
	req := &requests.UpdateContractorRequest{
		ResidenceID: &newResidenceID,
	}

	_, err := service.UpdateContractor(contractor.ID, attacker.ID, req)
	require.Error(t, err, "should not allow reassigning contractor to a residence the user has no access to")
	testutil.AssertAppErrorCode(t, err, http.StatusForbidden)
}

func TestUpdateContractor_SameResidence_Succeeds(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	residence1 := testutil.CreateTestResidence(t, db, owner.ID, "House 1")
	residence2 := testutil.CreateTestResidence(t, db, owner.ID, "House 2")
	contractor := testutil.CreateTestContractor(t, db, residence1.ID, owner.ID, "My Contractor")

	// Owner reassigns contractor to their other residence - should succeed
	newResidenceID := residence2.ID
	newName := "Updated Contractor"
	req := &requests.UpdateContractorRequest{
		Name:        &newName,
		ResidenceID: &newResidenceID,
	}

	resp, err := service.UpdateContractor(contractor.ID, owner.ID, req)
	require.NoError(t, err, "should allow reassigning contractor to a residence the user owns")
	require.NotNil(t, resp)
	require.Equal(t, "Updated Contractor", resp.Name)
}

func TestUpdateContractor_RemoveResidence_Succeeds(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "My House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, owner.ID, "My Contractor")

	// Setting ResidenceID to nil should remove the residence association (make it personal)
	req := &requests.UpdateContractorRequest{
		ResidenceID: nil,
	}

	resp, err := service.UpdateContractor(contractor.ID, owner.ID, req)
	require.NoError(t, err, "should allow removing residence association")
	require.NotNil(t, resp)
}

// === UpdateContractor — partial update multiple fields ===

func TestContractorService_UpdateContractor_PartialUpdate(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Original Name")

	newName := "Updated Plumber"
	newPhone := "555-9999"
	newEmail := "new@plumber.com"
	newCompany := "Best Plumbing"
	newWebsite := "https://bestplumbing.com"
	newNotes := "Great work"
	newStreet := "456 Plumber Ave"
	newCity := "Dallas"
	newState := "TX"
	newPostal := "75001"
	rating := 5.0
	isFav := true

	req := &requests.UpdateContractorRequest{
		Name:          &newName,
		Phone:         &newPhone,
		Email:         &newEmail,
		Company:       &newCompany,
		Website:       &newWebsite,
		Notes:         &newNotes,
		StreetAddress: &newStreet,
		City:          &newCity,
		StateProvince: &newState,
		PostalCode:    &newPostal,
		Rating:        &rating,
		IsFavorite:    &isFav,
		ResidenceID:   &residence.ID,
	}

	resp, err := service.UpdateContractor(contractor.ID, user.ID, req)
	require.NoError(t, err)
	assert.Equal(t, "Updated Plumber", resp.Name)
	assert.Equal(t, "555-9999", resp.Phone)
	assert.Equal(t, "new@plumber.com", resp.Email)
	assert.True(t, resp.IsFavorite)
}

// === UpdateContractor — with specialties ===

func TestContractorService_UpdateContractor_WithSpecialties(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")
	contractor := testutil.CreateTestContractor(t, db, residence.ID, user.ID, "Test Contractor")

	// Get specialty IDs from seeded data
	var specialties []models.ContractorSpecialty
	err := db.Find(&specialties).Error
	require.NoError(t, err)
	require.NotEmpty(t, specialties)

	specialtyIDs := []uint{specialties[0].ID, specialties[1].ID}
	req := &requests.UpdateContractorRequest{
		SpecialtyIDs: specialtyIDs,
		ResidenceID:  &residence.ID,
	}

	resp, err := service.UpdateContractor(contractor.ID, user.ID, req)
	require.NoError(t, err)
	assert.NotNil(t, resp)
}

// === CreateContractor — with specialties ===

func TestContractorService_CreateContractor_WithSpecialties(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")

	var specialties []models.ContractorSpecialty
	err := db.Find(&specialties).Error
	require.NoError(t, err)

	req := &requests.CreateContractorRequest{
		ResidenceID:  &residence.ID,
		Name:         "Specialized Plumber",
		SpecialtyIDs: []uint{specialties[0].ID},
	}

	resp, err := service.CreateContractor(req, user.ID)
	require.NoError(t, err)
	assert.Equal(t, "Specialized Plumber", resp.Name)
}

// === ListContractors — empty result ===

func TestContractorService_ListContractors_Empty(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	// No residence, no contractors
	resp, err := service.ListContractors(user.ID)
	require.NoError(t, err)
	assert.Empty(t, resp)
}

// === ListContractorsByResidence — empty result ===

func TestContractorService_ListContractorsByResidence_Empty(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Empty House")

	resp, err := service.ListContractorsByResidence(residence.ID, user.ID)
	require.NoError(t, err)
	assert.Empty(t, resp)
}

// === Personal contractor access — creator has access, others don't ===

func TestContractorService_PersonalContractor_OnlyCreatorAccess(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	contractorRepo := repositories.NewContractorRepository(db)
	residenceRepo := repositories.NewResidenceRepository(db)
	service := NewContractorService(contractorRepo, residenceRepo)

	creator := testutil.CreateTestUser(t, db, "creator", "creator@test.com", "Password123")
	other := testutil.CreateTestUser(t, db, "other", "other@test.com", "Password123")

	// Create personal contractor (no residence)
	req := &requests.CreateContractorRequest{
		Name: "Personal Plumber",
	}
	resp, err := service.CreateContractor(req, creator.ID)
	require.NoError(t, err)

	// Creator can access
	_, err = service.GetContractor(resp.ID, creator.ID)
	require.NoError(t, err)

	// Other user cannot
	_, err = service.GetContractor(resp.ID, other.ID)
	testutil.AssertAppError(t, err, http.StatusForbidden, "error.contractor_access_denied")
}
