package repositories

import (
	"testing"

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

	"github.com/treytartt/honeydue-api/internal/testutil"
)

func TestResidenceRepository_FindByIDSimple(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

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

	found, err := repo.FindByIDSimple(residence.ID)
	require.NoError(t, err)
	assert.Equal(t, residence.ID, found.ID)
	assert.Equal(t, "Test House", found.Name)
}

func TestResidenceRepository_FindByIDSimple_NotFound(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

	_, err := repo.FindByIDSimple(9999)
	assert.Error(t, err)
}

func TestResidenceRepository_FindByIDSimple_InactiveNotFound(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

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

	db.Model(residence).Update("is_active", false)

	_, err := repo.FindByIDSimple(residence.ID)
	assert.Error(t, err)
}

func TestResidenceRepository_FindResidenceIDsByUser(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

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

	r1 := testutil.CreateTestResidence(t, db, owner.ID, "House 1")
	r2 := testutil.CreateTestResidence(t, db, owner.ID, "House 2")

	repo.AddUser(r1.ID, sharedUser.ID)

	// Owner should see both
	ownerIDs, err := repo.FindResidenceIDsByUser(owner.ID)
	require.NoError(t, err)
	assert.Len(t, ownerIDs, 2)

	// Shared user should see one
	sharedIDs, err := repo.FindResidenceIDsByUser(sharedUser.ID)
	require.NoError(t, err)
	assert.Len(t, sharedIDs, 1)
	assert.Equal(t, r1.ID, sharedIDs[0])

	_ = r2 // suppress unused
}

func TestResidenceRepository_FindResidenceIDsByOwner(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

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

	testutil.CreateTestResidence(t, db, owner.ID, "House 1")
	testutil.CreateTestResidence(t, db, owner.ID, "House 2")
	testutil.CreateTestResidence(t, db, otherOwner.ID, "Other House")

	ids, err := repo.FindResidenceIDsByOwner(owner.ID)
	require.NoError(t, err)
	assert.Len(t, ids, 2)
}

func TestResidenceRepository_DeactivateShareCode(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

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

	// Create a share code
	created, err := repo.CreateShareCode(residence.ID, owner.ID, 24*60*60*1000*1000*1000) // 24h in ns
	require.NoError(t, err)
	assert.True(t, created.IsActive)

	// Deactivate it
	err = repo.DeactivateShareCode(created.ID)
	require.NoError(t, err)

	// FindShareCodeByCode should not find it
	_, err = repo.FindShareCodeByCode(created.Code)
	assert.Error(t, err)
}

func TestResidenceRepository_GetActiveShareCode(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

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

	// No active code initially
	code, err := repo.GetActiveShareCode(residence.ID)
	require.NoError(t, err)
	assert.Nil(t, code)

	// Create a share code
	created, err := repo.CreateShareCode(residence.ID, owner.ID, 24*60*60*1000*1000*1000)
	require.NoError(t, err)

	// Should find the active code
	code, err = repo.GetActiveShareCode(residence.ID)
	require.NoError(t, err)
	require.NotNil(t, code)
	assert.Equal(t, created.Code, code.Code)
}

func TestResidenceRepository_CreateShareCode_DeactivatesPrevious(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

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

	// Create first code
	first, err := repo.CreateShareCode(residence.ID, owner.ID, 24*60*60*1000*1000*1000)
	require.NoError(t, err)

	// Create second code (should deactivate first)
	second, err := repo.CreateShareCode(residence.ID, owner.ID, 24*60*60*1000*1000*1000)
	require.NoError(t, err)

	assert.NotEqual(t, first.Code, second.Code)

	// First code should no longer be findable
	_, err = repo.FindShareCodeByCode(first.Code)
	assert.Error(t, err)

	// Second code should be active
	found, err := repo.FindShareCodeByCode(second.Code)
	require.NoError(t, err)
	assert.Equal(t, second.Code, found.Code)
}

func TestResidenceRepository_FindResidenceTypeByID(t *testing.T) {
	db := testutil.SetupTestDB(t)
	testutil.SeedLookupData(t, db)
	repo := NewResidenceRepository(db)

	// Get first residence type
	types, err := repo.GetAllResidenceTypes()
	require.NoError(t, err)
	require.NotEmpty(t, types)

	found, err := repo.FindResidenceTypeByID(types[0].ID)
	require.NoError(t, err)
	assert.Equal(t, types[0].Name, found.Name)
}

func TestResidenceRepository_FindResidenceTypeByID_NotFound(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

	_, err := repo.FindResidenceTypeByID(9999)
	assert.Error(t, err)
}

func TestResidenceRepository_GetTasksForReport(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

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

	testutil.CreateTestTask(t, db, residence.ID, user.ID, "Task 1")
	testutil.CreateTestTask(t, db, residence.ID, user.ID, "Task 2")

	tasks, err := repo.GetTasksForReport(residence.ID)
	require.NoError(t, err)
	assert.Len(t, tasks, 2)
}

func TestResidenceRepository_AddUser_Idempotent(t *testing.T) {
	db := testutil.SetupTestDB(t)
	repo := NewResidenceRepository(db)

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

	// Add same user twice (should not error due to ON CONFLICT DO NOTHING)
	err := repo.AddUser(residence.ID, sharedUser.ID)
	require.NoError(t, err)

	err = repo.AddUser(residence.ID, sharedUser.ID)
	require.NoError(t, err)

	// Should still only count once
	users, err := repo.GetResidenceUsers(residence.ID)
	require.NoError(t, err)
	assert.Len(t, users, 2) // owner + sharedUser
}
