package handlers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"testing"

	"github.com/labstack/echo/v4"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/treytartt/honeydue-api/internal/config"
	"github.com/treytartt/honeydue-api/internal/dto/requests"
	"github.com/treytartt/honeydue-api/internal/repositories"
	"github.com/treytartt/honeydue-api/internal/services"
	"github.com/treytartt/honeydue-api/internal/testutil"
	"gorm.io/gorm"
)

func setupResidenceHandler(t *testing.T) (*ResidenceHandler, *echo.Echo, *gorm.DB) {
	db := testutil.SetupTestDB(t)
	residenceRepo := repositories.NewResidenceRepository(db)
	userRepo := repositories.NewUserRepository(db)
	cfg := &config.Config{}
	residenceService := services.NewResidenceService(residenceRepo, userRepo, cfg)
	handler := NewResidenceHandler(residenceService, nil, nil, true)
	e := testutil.SetupTestRouter()
	return handler, e, db
}

func TestResidenceHandler_CreateResidence(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.POST("/", handler.CreateResidence)

	t.Run("successful creation", func(t *testing.T) {
		req := requests.CreateResidenceRequest{
			Name:          "My House",
			StreetAddress: "123 Main St",
			City:          "Austin",
			StateProvince: "TX",
			PostalCode:    "78701",
		}

		w := testutil.MakeRequest(e, "POST", "/api/residences/", req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusCreated)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		// Response should be wrapped in WithSummaryResponse
		assert.Contains(t, response, "data")
		assert.Contains(t, response, "summary")

		residenceData := response["data"].(map[string]interface{})
		assert.Equal(t, "My House", residenceData["name"])
		assert.Equal(t, "123 Main St", residenceData["street_address"])
		assert.Equal(t, "Austin", residenceData["city"])
		assert.Equal(t, "TX", residenceData["state_province"])
		assert.Equal(t, "78701", residenceData["postal_code"])
		assert.Equal(t, "USA", residenceData["country"]) // Default
		assert.Equal(t, true, residenceData["is_primary"])
	})

	t.Run("creation with optional fields", func(t *testing.T) {
		bedrooms := 3
		bathrooms := decimal.NewFromFloat(2.5)
		sqft := 2000
		isPrimary := false

		req := requests.CreateResidenceRequest{
			Name:          "Second House",
			StreetAddress: "456 Oak Ave",
			City:          "Dallas",
			StateProvince: "TX",
			PostalCode:    "75001",
			Country:       "USA",
			Bedrooms:      &bedrooms,
			Bathrooms:     &bathrooms,
			SquareFootage: &sqft,
			IsPrimary:     &isPrimary,
		}

		w := testutil.MakeRequest(e, "POST", "/api/residences/", req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusCreated)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		// Response should be wrapped in WithSummaryResponse
		assert.Contains(t, response, "data")
		assert.Contains(t, response, "summary")

		residenceData := response["data"].(map[string]interface{})
		assert.Equal(t, float64(3), residenceData["bedrooms"])
		assert.Equal(t, "2.5", residenceData["bathrooms"]) // Decimal serializes as string
		assert.Equal(t, float64(2000), residenceData["square_footage"])
		// Note: first residence becomes primary by default even if is_primary=false is specified
		assert.Contains(t, []interface{}{true, false}, residenceData["is_primary"])
	})

	t.Run("creation with missing required fields", func(t *testing.T) {
		// Only name is required; address fields are optional
		req := map[string]string{
			// Missing name - this is required
		}

		w := testutil.MakeRequest(e, "POST", "/api/residences/", req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusBadRequest)
	})
}

func TestResidenceHandler_GetResidence(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	otherUser := testutil.CreateTestUser(t, db, "other", "other@test.com", "password")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Test House")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.GET("/:id/", handler.GetResidence)

	otherAuthGroup := e.Group("/api/other-residences")
	otherAuthGroup.Use(testutil.MockAuthMiddleware(otherUser))
	otherAuthGroup.GET("/:id/", handler.GetResidence)

	t.Run("get own residence", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", fmt.Sprintf("/api/residences/%d/", residence.ID), nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		assert.Equal(t, "Test House", response["name"])
		assert.Equal(t, float64(residence.ID), response["id"])
	})

	t.Run("get residence with invalid ID", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", "/api/residences/invalid/", nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusBadRequest)
	})

	t.Run("get non-existent residence", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", "/api/residences/9999/", nil, "test-token")

		// Returns 403 (access denied) rather than 404 to not reveal whether an ID exists
		testutil.AssertStatusCode(t, w, http.StatusForbidden)
	})

	t.Run("access denied for other user", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", fmt.Sprintf("/api/other-residences/%d/", residence.ID), nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusForbidden)
	})
}

func TestResidenceHandler_ListResidences(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	testutil.CreateTestResidence(t, db, user.ID, "House 1")
	testutil.CreateTestResidence(t, db, user.ID, "House 2")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.GET("/", handler.ListResidences)

	t.Run("list residences", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", "/api/residences/", nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response []map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		assert.Len(t, response, 2)
	})
}

func TestResidenceHandler_UpdateResidence(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	sharedUser := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "password")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Original Name")

	// Share with user
	residenceRepo := repositories.NewResidenceRepository(db)
	residenceRepo.AddUser(residence.ID, sharedUser.ID)

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.PUT("/:id/", handler.UpdateResidence)

	sharedGroup := e.Group("/api/shared-residences")
	sharedGroup.Use(testutil.MockAuthMiddleware(sharedUser))
	sharedGroup.PUT("/:id/", handler.UpdateResidence)

	t.Run("owner can update", func(t *testing.T) {
		newName := "Updated Name"
		newCity := "Dallas"
		req := requests.UpdateResidenceRequest{
			Name: &newName,
			City: &newCity,
		}

		w := testutil.MakeRequest(e, "PUT", fmt.Sprintf("/api/residences/%d/", residence.ID), req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		// Response should be wrapped in WithSummaryResponse
		assert.Contains(t, response, "data")
		assert.Contains(t, response, "summary")

		residenceData := response["data"].(map[string]interface{})
		assert.Equal(t, "Updated Name", residenceData["name"])
		assert.Equal(t, "Dallas", residenceData["city"])
	})

	t.Run("shared user cannot update", func(t *testing.T) {
		newName := "Hacked Name"
		req := requests.UpdateResidenceRequest{
			Name: &newName,
		}

		w := testutil.MakeRequest(e, "PUT", fmt.Sprintf("/api/shared-residences/%d/", residence.ID), req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusForbidden)
	})
}

func TestResidenceHandler_DeleteResidence(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	sharedUser := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "password")
	residence := testutil.CreateTestResidence(t, db, user.ID, "To Delete")

	residenceRepo := repositories.NewResidenceRepository(db)
	residenceRepo.AddUser(residence.ID, sharedUser.ID)

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.DELETE("/:id/", handler.DeleteResidence)

	sharedGroup := e.Group("/api/shared-residences")
	sharedGroup.Use(testutil.MockAuthMiddleware(sharedUser))
	sharedGroup.DELETE("/:id/", handler.DeleteResidence)

	t.Run("shared user cannot delete", func(t *testing.T) {
		w := testutil.MakeRequest(e, "DELETE", fmt.Sprintf("/api/shared-residences/%d/", residence.ID), nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusForbidden)
	})

	t.Run("owner can delete", func(t *testing.T) {
		w := testutil.MakeRequest(e, "DELETE", fmt.Sprintf("/api/residences/%d/", residence.ID), nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		// Response should be wrapped in WithSummaryResponse
		assert.Contains(t, response, "data")
		assert.Contains(t, response, "summary")
		assert.Contains(t, response["data"], "deleted")
	})
}

func TestResidenceHandler_GenerateShareCode(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Share Test")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.POST("/:id/generate-share-code/", handler.GenerateShareCode)

	t.Run("generate share code", func(t *testing.T) {
		req := requests.GenerateShareCodeRequest{
			ExpiresInHours: 24,
		}

		w := testutil.MakeRequest(e, "POST", fmt.Sprintf("/api/residences/%d/generate-share-code/", residence.ID), req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		shareCode := response["share_code"].(map[string]interface{})
		code := shareCode["code"].(string)
		assert.Len(t, code, 6)
		assert.NotEmpty(t, shareCode["expires_at"])
	})
}

func TestResidenceHandler_JoinWithCode(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	newUser := testutil.CreateTestUser(t, db, "newuser", "new@test.com", "password")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Join Test")

	// Generate share code first
	residenceRepo := repositories.NewResidenceRepository(db)
	userRepo := repositories.NewUserRepository(db)
	cfg := &config.Config{}
	residenceService := services.NewResidenceService(residenceRepo, userRepo, cfg)
	shareResp, _ := residenceService.GenerateShareCode(residence.ID, owner.ID, 24)

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(newUser))
	authGroup.POST("/join-with-code/", handler.JoinWithCode)

	ownerGroup := e.Group("/api/owner-residences")
	ownerGroup.Use(testutil.MockAuthMiddleware(owner))
	ownerGroup.POST("/join-with-code/", handler.JoinWithCode)

	t.Run("join with valid code", func(t *testing.T) {
		req := requests.JoinWithCodeRequest{
			Code: shareResp.ShareCode.Code,
		}

		w := testutil.MakeRequest(e, "POST", "/api/residences/join-with-code/", req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		// JoinResidenceResponse includes summary
		assert.Contains(t, response, "residence")
		assert.Contains(t, response, "summary")

		residenceResp := response["residence"].(map[string]interface{})
		assert.Equal(t, "Join Test", residenceResp["name"])
	})

	t.Run("owner tries to join own residence", func(t *testing.T) {
		// Generate new code
		shareResp2, _ := residenceService.GenerateShareCode(residence.ID, owner.ID, 24)

		req := requests.JoinWithCodeRequest{
			Code: shareResp2.ShareCode.Code,
		}

		w := testutil.MakeRequest(e, "POST", "/api/owner-residences/join-with-code/", req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusConflict)
	})

	t.Run("join with invalid code", func(t *testing.T) {
		req := requests.JoinWithCodeRequest{
			Code: "ABCDEF", // Valid length (6) but non-existent code
		}

		w := testutil.MakeRequest(e, "POST", "/api/residences/join-with-code/", req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusNotFound)
	})
}

func TestResidenceHandler_GetResidenceUsers(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	sharedUser := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "password")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Users Test")

	residenceRepo := repositories.NewResidenceRepository(db)
	residenceRepo.AddUser(residence.ID, sharedUser.ID)

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(owner))
	authGroup.GET("/:id/users/", handler.GetResidenceUsers)

	t.Run("get residence users", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", fmt.Sprintf("/api/residences/%d/users/", residence.ID), nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response []map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		assert.Len(t, response, 2) // owner + shared user
	})
}

func TestResidenceHandler_RemoveUser(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
	sharedUser := testutil.CreateTestUser(t, db, "shared", "shared@test.com", "password")
	residence := testutil.CreateTestResidence(t, db, owner.ID, "Remove Test")

	residenceRepo := repositories.NewResidenceRepository(db)
	residenceRepo.AddUser(residence.ID, sharedUser.ID)

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(owner))
	authGroup.DELETE("/:id/users/:user_id/", handler.RemoveResidenceUser)

	t.Run("remove shared user", func(t *testing.T) {
		w := testutil.MakeRequest(e, "DELETE", fmt.Sprintf("/api/residences/%d/users/%d/", residence.ID, sharedUser.ID), nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		response := testutil.ParseJSON(t, w.Body.Bytes())
		assert.Contains(t, response["message"], "removed")
	})

	t.Run("cannot remove owner", func(t *testing.T) {
		w := testutil.MakeRequest(e, "DELETE", fmt.Sprintf("/api/residences/%d/users/%d/", residence.ID, owner.ID), nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusBadRequest)
	})
}

func TestResidenceHandler_GetResidenceTypes(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	testutil.SeedLookupData(t, db)
	user := testutil.CreateTestUser(t, db, "user", "user@test.com", "password")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.GET("/types/", handler.GetResidenceTypes)

	t.Run("get residence types", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", "/api/residences/types/", nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response []map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		assert.Greater(t, len(response), 0)
	})
}

func TestResidenceHandler_JSONResponses(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.POST("/", handler.CreateResidence)
	authGroup.GET("/", handler.ListResidences)

	t.Run("residence response has correct JSON structure", func(t *testing.T) {
		req := requests.CreateResidenceRequest{
			Name:          "JSON Test House",
			StreetAddress: "123 Test St",
			City:          "Austin",
			StateProvince: "TX",
			PostalCode:    "78701",
		}

		w := testutil.MakeRequest(e, "POST", "/api/residences/", req, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusCreated)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		// Response should be wrapped in WithSummaryResponse
		assert.Contains(t, response, "data")
		assert.Contains(t, response, "summary")

		residenceData := response["data"].(map[string]interface{})

		// Required fields in residence data
		assert.Contains(t, residenceData, "id")
		assert.Contains(t, residenceData, "name")
		assert.Contains(t, residenceData, "street_address")
		assert.Contains(t, residenceData, "city")
		assert.Contains(t, residenceData, "state_province")
		assert.Contains(t, residenceData, "postal_code")
		assert.Contains(t, residenceData, "country")
		assert.Contains(t, residenceData, "is_primary")
		assert.Contains(t, residenceData, "is_active")
		assert.Contains(t, residenceData, "created_at")
		assert.Contains(t, residenceData, "updated_at")

		// Type checks
		assert.IsType(t, float64(0), residenceData["id"])
		assert.IsType(t, "", residenceData["name"])
		assert.IsType(t, true, residenceData["is_primary"])

		// Summary should have expected fields
		summary := response["summary"].(map[string]interface{})
		assert.Contains(t, summary, "total_residences")
		assert.Contains(t, summary, "total_tasks")
	})

	t.Run("list response returns array", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", "/api/residences/", nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response []map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		// Response should be an array of residences
		assert.IsType(t, []map[string]interface{}{}, response)
	})
}

func TestResidenceHandler_CreateResidence_NegativeBedrooms_Returns400(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.POST("/", handler.CreateResidence)

	t.Run("negative bedrooms rejected", func(t *testing.T) {
		bedrooms := -1
		req := requests.CreateResidenceRequest{
			Name:     "Bad House",
			Bedrooms: &bedrooms,
		}

		w := testutil.MakeRequest(e, "POST", "/api/residences/", req, "test-token")
		testutil.AssertStatusCode(t, w, http.StatusBadRequest)
	})

	t.Run("negative square footage rejected", func(t *testing.T) {
		sqft := -100
		req := requests.CreateResidenceRequest{
			Name:          "Bad House",
			SquareFootage: &sqft,
		}

		w := testutil.MakeRequest(e, "POST", "/api/residences/", req, "test-token")
		testutil.AssertStatusCode(t, w, http.StatusBadRequest)
	})

	t.Run("zero bedrooms accepted", func(t *testing.T) {
		bedrooms := 0
		req := requests.CreateResidenceRequest{
			Name:     "Studio Apartment",
			Bedrooms: &bedrooms,
		}

		w := testutil.MakeRequest(e, "POST", "/api/residences/", req, "test-token")
		testutil.AssertStatusCode(t, w, http.StatusCreated)
	})
}

func TestResidenceHandler_GetMyResidences(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	testutil.CreateTestResidence(t, db, user.ID, "House 1")
	testutil.CreateTestResidence(t, db, user.ID, "House 2")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.GET("/my-residences/", handler.GetMyResidences)

	t.Run("successful my residences", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", "/api/residences/my-residences/", nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		// GetMyResidences returns MyResidencesResponse: {"residences": [...]}
		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		residences := response["residences"].([]interface{})
		assert.Len(t, residences, 2)
	})

	t.Run("user with no residences returns empty", func(t *testing.T) {
		noResUser := testutil.CreateTestUser(t, db, "nores", "nores@test.com", "Password123")

		e2 := testutil.SetupTestRouter()
		authGroup2 := e2.Group("/api/residences")
		authGroup2.Use(testutil.MockAuthMiddleware(noResUser))
		authGroup2.GET("/my-residences/", handler.GetMyResidences)

		w := testutil.MakeRequest(e2, "GET", "/api/residences/my-residences/", nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		// GetMyResidences returns MyResidencesResponse: {"residences": [...] or null}
		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)
		if response["residences"] == nil {
			// null residences means no residences
		} else {
			residences := response["residences"].([]interface{})
			assert.Len(t, residences, 0)
		}
	})
}

func TestResidenceHandler_GetSummary(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	testutil.CreateTestResidence(t, db, user.ID, "House 1")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.GET("/summary/", handler.GetSummary)

	t.Run("successful summary", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", "/api/residences/summary/", nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		assert.Contains(t, response, "total_residences")
		assert.Contains(t, response, "total_tasks")
	})
}

func TestResidenceHandler_UpdateResidence_InvalidID(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.PUT("/:id/", handler.UpdateResidence)

	t.Run("invalid id returns 400", func(t *testing.T) {
		newName := "Updated"
		req := requests.UpdateResidenceRequest{Name: &newName}
		w := testutil.MakeRequest(e, "PUT", "/api/residences/invalid/", req, "test-token")
		testutil.AssertStatusCode(t, w, http.StatusBadRequest)
	})

	t.Run("non-existent id returns 403", func(t *testing.T) {
		newName := "Updated"
		req := requests.UpdateResidenceRequest{Name: &newName}
		w := testutil.MakeRequest(e, "PUT", "/api/residences/9999/", req, "test-token")
		testutil.AssertStatusCode(t, w, http.StatusForbidden)
	})
}

func TestResidenceHandler_DeleteResidence_InvalidID(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.DELETE("/:id/", handler.DeleteResidence)

	t.Run("invalid id returns 400", func(t *testing.T) {
		w := testutil.MakeRequest(e, "DELETE", "/api/residences/invalid/", nil, "test-token")
		testutil.AssertStatusCode(t, w, http.StatusBadRequest)
	})

	t.Run("non-existent id returns 403", func(t *testing.T) {
		w := testutil.MakeRequest(e, "DELETE", "/api/residences/9999/", nil, "test-token")
		testutil.AssertStatusCode(t, w, http.StatusForbidden)
	})
}

func TestResidenceHandler_GetShareCode(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Share Code Test")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.GET("/:id/share-code/", handler.GetShareCode)

	t.Run("no share code returns null", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", fmt.Sprintf("/api/residences/%d/share-code/", residence.ID), nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)
		assert.Nil(t, response["share_code"])
	})

	t.Run("invalid id returns 400", func(t *testing.T) {
		w := testutil.MakeRequest(e, "GET", "/api/residences/invalid/share-code/", nil, "test-token")
		testutil.AssertStatusCode(t, w, http.StatusBadRequest)
	})
}

func TestResidenceHandler_GenerateSharePackage(t *testing.T) {
	handler, e, db := setupResidenceHandler(t)
	user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
	residence := testutil.CreateTestResidence(t, db, user.ID, "Package Test")

	authGroup := e.Group("/api/residences")
	authGroup.Use(testutil.MockAuthMiddleware(user))
	authGroup.POST("/:id/generate-share-package/", handler.GenerateSharePackage)

	t.Run("generate share package", func(t *testing.T) {
		w := testutil.MakeRequest(e, "POST", fmt.Sprintf("/api/residences/%d/generate-share-package/", residence.ID), nil, "test-token")

		testutil.AssertStatusCode(t, w, http.StatusOK)

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)
		assert.Contains(t, response, "share_code")
	})
}
