package main

import (
	"bookhoard/internal/database"
	"bookhoard/internal/handlers"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgtype"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// TestWithStructs demonstrates struct-based testing patterns
// BEFORE: map[string]interface{}  ->  AFTER: handlers.* structs
// BEFORE: No DB verification    ->  AFTER: Database verification

func TestWithStructs(t *testing.T) {
	deviceSetup := setupDeviceTest(t)
	token := deviceSetup.UserToken

	// Create a device to test listing
	device := deviceSetup.CreateDevice(t, "Test Device", "koreader", "test-device-struct-123")

	t.Run("ListDevices with struct", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/devices", nil)
		req.Header.Set("Authorization", "Bearer "+token)
		rr := httptest.NewRecorder()
		deviceSetup.Server.Config.Handler.ServeHTTP(rr, req)

		assert.Equal(t, http.StatusOK, rr.Code, "Should list devices")

		var response handlers.DeviceListResponse
		err := json.Unmarshal(rr.Body.Bytes(), &response)
		require.NoError(t, err, "Response should match DeviceListResponse schema")
		assert.GreaterOrEqual(t, len(response.Devices), 1, "Should have at least one device")

		firstDevice := response.Devices[0]
		assert.Equal(t, device.Name, firstDevice.DeviceName, "Should match device name")
		assert.Equal(t, device.Type, firstDevice.DeviceType, "Should match device type")
	})

	t.Run("UpdateDevice with struct and DB verification", func(t *testing.T) {
		syncEnabled := false
		syncFreq := int32(15)

		updateRequest := map[string]interface{}{
			"sync_enabled":           &syncEnabled,
			"sync_frequency_minutes": &syncFreq,
		}
		updateBody, _ := json.Marshal(updateRequest)

		req := httptest.NewRequest("PUT", fmt.Sprintf("/api/devices/%s", device.ID), bytes.NewBuffer(updateBody))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Bearer "+token)
		rr := httptest.NewRecorder()
		deviceSetup.Server.Config.Handler.ServeHTTP(rr, req)

		assert.Equal(t, http.StatusOK, rr.Code, "Should update device")

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

		assert.True(t, response["device_updated"].(bool), "Device should be updated")

		// NEW: Database verification
		pgDeviceID := pgtype.UUID{Bytes: [16]byte(device.ID), Valid: true}
		updatedDevice, err := deviceSetup.DB.GetDevice(context.Background(), pgDeviceID)
		require.NoError(t, err, "Device should exist in database after update")

		assert.Equal(t, syncEnabled, updatedDevice.SyncEnabled.Bool, "DB: Sync should be disabled")
		assert.Equal(t, syncFreq, updatedDevice.SyncFrequencyMinutes.Int32, "DB: Sync frequency should be updated")
	})
}

// verifyDeviceUpdated is a helper function for database verification after device updates
func verifyDeviceUpdated(t *testing.T, db *database.Queries, deviceID uuid.UUID, syncEnabled bool, syncFreq int32) {
	pgDeviceID := pgtype.UUID{Bytes: [16]byte(deviceID), Valid: true}
	device, err := db.GetDevice(context.Background(), pgDeviceID)
	require.NoError(t, err, "Device should exist in database")

	assert.Equal(t, syncEnabled, device.SyncEnabled.Bool, "DB: Sync enabled should match")
	assert.Equal(t, syncFreq, device.SyncFrequencyMinutes.Int32, "DB: Sync frequency should match")
}
